LINQ to XML
LINQ to XML предоставя нов програмен модел за
четене, записване и конструиране на XML. Този модел е
много по-удобен от DOM API (на XmlDocument), което се използва до момента, а и използва много по-малко памет. Освен
това е много по-лесен за използване от XmlReader/XmlWriter. За да се постигне това
улеснение са създадени нов набор от класове за работа с XML в пространството от имена System.Xml.Linq в асемблито System.Xml.Linq.dll.
Четене и обхождане на XML
Нека да
разгледаме следният xml документ:
<?xml version="1.0" encoding="utf-8"?>
<Products>
<Product ProductName="Alice Mutton">
<ProductID>17</ProductID>
<UnitPrice>39.0000</UnitPrice>
<UnitsInStock>0</UnitsInStock>
</Product>
<Product ProductName="Aniseed Syrup">
<ProductID>3</ProductID>
<UnitPrice>10.0000</UnitPrice>
<UnitsInStock>13</UnitsInStock>
</Product>
</Products>
|
Този фрагмент
съдържа основен (root) елемент <Products>, който съдържа два поделемента от тип <Product>. Данните от елементите <Product> са съхранени по два начина – като атрибути и като поделементи. С
помощта на следния израз можем да прочетем документа и да го покажем на в
конзолата:
XElement xml = XElement.Load(@"D:\LINQTutorial\Products.xml");
var products = from p in
xml.Descendants("Product")
orderby p.Element("ProductID").Value descending
select new {
ProductName = p.Attribute("ProductName").Value,
ProductID = p.Element("ProductID").Value,
UnitPrice = p.Element("UnitPrice").Value,
UnitsInStock = p.Element("UnitsInStock").Value
};
foreach (var item in products) {
Console.WriteLine("{0}\t{1}\t{2}\t{3}",
item.ProductID, item.ProductName, item.UnitsInStock, item.UnitPrice);
}
|
На първият ред създаваме инстанция от тип XElement, зареждайки XML
документа от файловата система. Следва по-интересната част –
самата LINQ заявка.
От първият ред на заявката определяме
основната променлива р (отново от тип XElement), която ще представлява всеки елемент върнат от израза xml.Descendants("Product"). Метода връща всички поделементи от тип <Product>. За да извлечем данните използваме два метода – XElement.Element() и XElement.Attribute(), съответно, за да
извлечем данните съответно от поделемените и от атрибутите. Забележете, че тези
два метода отново връщат резултат от тип XElement и за да извлечем данните трябва да използваме свойството Value.
Тук обаче се
крие опасност – тъй като подаваме имената на елементите и атрибутите като текст
има възможност за грешка, а е възможно също така даден елемент да не съдържа
всички поделементи и атрибути. В този случай XElement.Element()
и XElement.Attribute() ще няма да намерят търсеният елемент/атрибут и ще върнат null. Когато се опитаме да вземем
стойността на свойството Value
ще получим NullReferenceException. За да избегнем подобна ситуация може да използваме помощен метод:
var products = from p in
xml.Descendants("Product")
orderby p.Element("ProductID").Value descending
select new {
ProductName = GetXElementValue(p.Attribute("ProductName")),
ProductID = GetXElementValue(p.Element("ProductID")),
UnitPrice = GetXElementValue(p.Element("UnitPrice")),
UnitsInStock = GetXElementValue(p.Element("UnitsInStock")),
};
private string
GetXElementValue(XElement el) {
if (null != el) return el.Value;
return string.Empty;
}
|
По този начин се подсигуряваме, че няма да
възникне изключение.
Създаване на XML
Освен
прочитането и обхождането на XML документ, можем много
лесно да генерираме такъв. Понеже класът XElement е много гъвкав позволява генерирането на цял документ с един израз:
XElement e = new XElement("Products",
new XElement("Product",
new XAttribute("ProductName", "Product1"),
new XElement("ProductID", 1),
new XElement("UnitPrice", 11.5),
new XElement("UnitsInStock", 12)),
new XElement("Product",
new XAttribute("ProductName", "Product2"),
new XElement("ProductID", 2),
new XElement("UnitPrice", 2.45),
new XElement("UnitsInStock", 89))
);
|
Въпреки, че
класът XElement е нов горната
конструкция не е нова – в нея използваме комбинация от два конструктура на XElement, за да постигнем целта:
public XElement(XName name, params object[] content)
public XElement(XName name, object content)
|
Тези два
конструктура ни позволяват да комбинираме LINQ to XML и
LINQ to SQL, за да експортираме данни към XML. В следващият пример комбинираме двата конструктура на XElement с зявка от LINQ to SQL, за да получим XML документ със същата структура,
какъвто показахме в началото на тази точка:
NorthwindDataContext db = new NorthwindDataContext();
XElement xml = new XElement("Products",
from p in db.Products
orderby
p.ProductName
select new XElement("Product",
new XAttribute("ProductName",
p.ProductName),
new XElement("ProductID",
p.ProductID),
new XElement("UnitPrice",
p.UnitPrice),
new XElement("UnitsInStock",
p.UnitsInStock))
);
xml.Save(@"D:\LINQTutorial\Products.xml");
|
В този пример вместо да подадем с код
поредицата от параметри за конструктура, изискващ params
object[] content, подаваме резултат от LINQ to SQL заявка, която връща инстанции на XElement.
Това показва силата на LINQ и неговите клонове. Със сигурност ако трябваше да изпълним тази задача
с помощта на C# 2.0 кодът нямаше да е толкова малко и така
подреден.
Следваща част: LINQ to DataSet