۱۳۸۸/۰۵/۲۱

خلاصه‌اي از LINQ to XML


در اين مقاله مروري سريع و كاربردي خواهيم داشت بر توانايي‌هاي مقدماتي LINQ to XML .

فايل Employee.XML را با محتويات زير در نظر بگيريد:

<Employees>
<Employee>
<Name>Vahid</Name>
<Phone>11111111</Phone>
<Department>IT</Department>
<Age>52</Age>
</Employee>
<Employee>
<Name>Farid</Name>
<Phone>124578963</Phone>
<Department>Civil</Department>
<Age>35</Age>
</Employee>
<Employee>
<Name>Mehdi</Name>
<Phone>1245788754</Phone>
<Department>HR</Department>
<Age>30</Age>
</Employee>
</Employees>

1- چگونه يك فايل XML را جهت استفاده توسط LINQ بارگذاري كنيم؟

قبل از شروع، اسمبلي System.Xml.Linq بايد به ارجاعات برنامه اضافه شود. سپس:

using System.Xml.Linq;

XDocument xDoc = XDocument.Load("Employee.xml");

2- اگر محتويات XML دريافتي به صورت رشته بود (مثلا از يك ديتابيس دريافت شد)، اكنون چگونه بايد آن‌را بارگذاري كرد؟

اين‌كار را با استفاده از يك StringReader به صورت زير مي‌توان انجام داد:

// loading XML from string
StringReader sr = new StringReader(stringXML);
XDocument xDoc = XDocument.Load(sr);

3- چگونه يك كوئري ساده شامل تمامي ركوردهاي Employee مجموعه Employees را تهيه كنيم؟

using System.Collections;

IEnumerable<XElement> empList = from e in xDoc.Root.Elements("Employee") select e;
توسط كوئري فوق، تمامي ركوردهاي كاركنان در يك Collection در اختيار ما خواهند بود. نكته‌ي مهم عبارت LINQ فوق، xDoc.Root.Elements("Employee") مي‌باشد. به اين صورت از xDoc بارگذاري شده، ابتدا Root و يا همان محتواي فايل XML را جهت بررسي انتخاب كرده و سپس گره‌هاي مرتبط با كاركنان را انتخاب مي‌كنيم.
اكنون كه مجموعه كاركنان توسط متغير empList در اختيار ما است، دسترسي به محتويات آن به سادگي زير خواهد بود:

foreach (XElement employee in empList)
{
foreach (XElement e in employee.Elements())
{
Console.WriteLine(e.Name + " = " + e.Value);
}
}
در اين‌جا حلقه خارجي اطلاعات كلي تمامي كاركنان را باز مي‌گرداند و حلقه داخلي اطلاعات يك گره دريافت شده را نمايش مي‌دهد.

4- كوئري بنويسيد كه اطلاعات تمامي كاركنان بخش HR را باز گرداند.

IEnumerable<XElement> hrList = from e in xDoc.Root.Elements("Employee")
where e.Element("Department").Value == "HR"
select e;

همانطور كه ملاحظه مي‌كنيد همانند عبارات SQL ، در تمامي عناصر متعلق به كاركنان، عناصري كه دپارتمان آن‌ها مساوي HR است بازگشت داده مي‌شود.

5- كوئري بنويسيد كه ليست تمامي كاركنان بالاي 30 سال را ارائه دهد.

IEnumerable<XElement> tList = from e in xDoc.Root.Elements("Employee")
where int.Parse(e.Element("Age").Value) > 30
select e;

چون حاصل e.Element("Age").Value يك رشته است، براي اعمال فيلترهاي عددي بايد اين رشته‌ها تبديل به عدد شوند. به همين جهت از int.Parse استفاده شده است.

6- كوئري بنويسيد كه ليست تمامي كاركنان بالاي 30 سال را مرتب شده بر اساس نام باز گرداند.

IEnumerable<XElement> tList = from e in xDoc.Root.Elements("Employee")
where int.Parse(e.Element("Age").Value) > 30
orderby e.Element("Name").Value
select e;
در اينجا همانند عبارات SQL از orderby جهت مرتب سازي بر اساس عناصر نام استفاده شده است.

7- تبديل نتيجه‌ي يك كوئري LINQ به ليستي از اشياء

مفهومي به سي شارپ 3 اضافه شده است به نام anonymous types . براي مثال:



توسط اين قابليت مي‌توان يك شيء را بدون نياز به تعريف ابتدايي آن ايجاد كرد و حتي از intelliSense موجود در IDE نيز بهره مند شد. اين نوع‌هاي ناشناس توسط واژه‌هاي كليدي new و var توليد مي‌شوند. كامپايلر به صورت خودكار براي هر anonymous type يك كلاس ايجاد مي‌كند.
دقيقا از همين توانايي در LINQ نيز مي‌توان استفاده نمود:

var empList = from e in xDoc.Root.Elements("Employee")
orderby e.Element("Name").Value
select new
{
Name = e.Element("Name").Value,
Phone = e.Element("Phone").Value,
Department = e.Element("Department").Value,
Age = int.Parse(e.Element("Age").Value)
};
در اين‌جا حاصل كوئري، تبديل به ليستي از اشياءanonymous مي‌شود. اكنون براي نمايش آن‌ها نيز مي‌توان از واژه كليدي var استفاده نمود كه از هر لحاظ نسبت به روش اعمال foreach بر روي Xelement ها كه در مثال 3 مشاهده كرديم خواناتر است:

foreach (var employee in empList)
{
Console.WriteLine("Name = " + employee.Name);
Console.WriteLine("Dep = " + employee.Department);
Console.WriteLine("Phone = " + employee.Phone);
Console.WriteLine("Age = " + employee.Age);
}
و البته بديهي است كه مي‌توان از anonymous types استفاده نكرد و دقيقا تعريف شيء را پيش از انتخاب آن نيز مشخص نمود. براي مثال:

public class Employee
{
public string Name { get; set; }
public string Phone { get; set; }
public string Department { get; set; }
public int Age { get; set; }
}
در اين حالت، قسمت select new عبارت LINQ ما به select new Employee تغيير خواهد كرد.
براي مثال اگر بخواهيم ليست دريافتي را به صورت يك ليست جنريك بازگشت دهيم خواهيم داشت:

public class Employee
{
public string Name { get; set; }
public string Phone { get; set; }
public string Department { get; set; }
public int Age { get; set; }
}

List<Employee> Get()
{
XDocument xDoc = XDocument.Load("Employee.xml");
var items =
from e in xDoc.Root.Elements("Employee")
orderby e.Element("Name").Value
select new Employee
{
Name = e.Element("Name").Value,
Phone = e.Element("Phone").Value,
Department = e.Element("Department").Value,
Age = int.Parse(e.Element("Age").Value)
};
return items.ToList();
}