1. 程式人生 > >Linq學習隨筆二------LINQ to XML

Linq學習隨筆二------LINQ to XML

program str arp -s {0} ocs pla ges mes

LINQ to XML

LINQ to XML provides an in-memory XML programming interface that leverages the .NET Language-Integrated Query (LINQ) Framework. LINQ to XML uses the latest .NET Framework language capabilities and is comparable to an updated, redesigned Document Object Model (DOM) XML programming interface.

Linq to XML是一種新的XML編程方法,可以讓XML代碼更簡潔,並且功能更強大。

以下示例代碼中會用到的xmlTemplate.xml

技術分享
<?xml version="1.0"?>  
<SanGuo ChaoDai="HanChao">
  <Shu Wang="LiuBei">
    <WuJiangs>
      <WuJiang XingBie="0" WuQi="QingLongYanYueDao">GuanYu</WuJiang>
      <WuJiang XingBie="0" WuQi="BaZhangSheMao"
>ZhangFei</WuJiang> <WuJiang XingBie="1" WuQi="Jian">SunShangXiang</WuJiang> </WuJiangs> <ChengChi> <ChengDu></ChengDu> <GuangDu></GuangDu> <XinDu></XinDu> </ChengChi> </Shu> <Wei Wang
="SunQuan"> <WuJiangs> <WuJiang XingBie="0" WuQi="Jian">ZhouYu</WuJiang> </WuJiangs> <ChengChi> <SuZhou></SuZhou> <HuZhou></HuZhou> <YangZhou></YangZhou> </ChengChi> </Wei> <Wu Wang="LiuBei"> <WuJiangs> <WuJiang XingBie="0" WuQi="DaDao">XuChu</WuJiang> <WuJiang XingBie="0" WuQi="ShuangJi">DianWei</WuJiang> </WuJiangs> <ChengChi> <DuYi></DuYi> <DaLiang></DaLiang> </ChengChi> </Wu> </SanGuo>
View Code

1.創建XML樹

構造方法XElement,這個類構造出來的相當於Xml裏的node節點,XAttribute類對應XML node中的attribute,具體使用方法可以參看下面的示例代碼

 1             XElement sanGuoXml = 
 2                 new XElement("SanGuo", 
 3                     new XAttribute("ChaoDai", "HanChao"),
 4                     new XElement("Shu", 
 5                         new XAttribute("Wang", "LiuBei"),
 6                         new XElement("WuJiang", 
 7                             new XElement("GuanYu", 
 8                                 new XAttribute("XingBie", "0"),
 9                                 new XAttribute("WuQi", "QingLongYanYueDao")),
10                             new XElement("ZhangFei",
11                                 new XAttribute("XingBie", "0"),
12                                 new XAttribute("WuQi", "BaZhangSheMao")),                            
13                             new XElement("SunShangXiang",
14                                 new XAttribute("XingBie", "1"),
15                                 new XAttribute("WuQi", "Jian"))
16                             )
17                         ),
18                     new XElement("Wei",
19                         new XAttribute("Wang", "SunQuan"),
20                         new XElement("WuJiang", 
21                             new XElement("ZhouYu", 
22                                 new XAttribute("XingBie", "0"),
23                                 new XAttribute("WuQi", "Jian"))
24                             )
25                         ),
26                     new XElement("Wu",
27                         new XAttribute("Wang", "LiuBei"),
28                         new XElement("WuJiang", 
29                             new XElement("XuChu", 
30                                 new XAttribute("XingBie", "0"),
31                                 new XAttribute("WuQi", "DaDao")),
32                             new XElement("DianWei",
33                                 new XAttribute("XingBie", "0"),
34                                 new XAttribute("WuQi", "ShuangJi"))
35                             )
36                         )
37                     );

輸出結果

<SanGuo ChaoDai="HanChao">
  <Shu Wang="LiuBei">
    <WuJiang>
      <GuanYu XingBie="0" WuQi="QingLongYanYueDao" />
      <ZhangFei XingBie="0" WuQi="QingLongYanYueDao" />
      <SunShangXiang XingBie="1" WuQi="Jian" />
    </WuJiang>
  </Shu>
  <Wei Wang="SunQuan">
    <WuJiang>
      <ZhouYu XingBie="0" WuQi="Jian" />
    </WuJiang>
  </Wei>
  <Wu Wang="LiuBei">
    <WuJiang>
      <XuChu XingBie="0" WuQi="DaDao" />
      <DianWei XingBie="0" WuQi="ShuangJi" />
    </WuJiang>
  </Wu>
</SanGuo>

2.Clone vs Attach

在將 XNode(包括 XElement)或 XAttribute 對象添加到新Tree時,如果新內容沒有父節點,則對象會被attach到 XML 樹。 如果新內容已經有父節點,並且是另一 XML 樹的一部分,則Clone新內容。 然後將新clone的內容附加到 XML 樹中,示例代碼如下。

 1             XElement xmlTree1 = new XElement("Root",
 2                 new XElement("Child1", 1)
 3             );
 4             XElement child2 = new XElement("Child2", 2);// Create an element that is not parented. 
 5             XElement xmlTree2 = new XElement("Root",
 6                 xmlTree1.Element("Child1"),
 7                 child2
 8             );
 9  
10             Console.WriteLine("Child1 was {0}", //output Child1 was cloned
11                 xmlTree1.Element("Child1") == xmlTree2.Element("Child1") ?
12                 "attached" : "cloned");  
13             Console.WriteLine("Child2 was {0}", //output Child2 was attached
14                 child2 == xmlTree2.Element("Child2") ?
15                 "attached" : "cloned");

3.通過讀取字符串或者File加載XML

1             XElement readyMadeXml1 = XElement.Parse(File.ReadAllText(@"..\..\xmlTemplate.xml"));
2             XElement readyMadeXml2 = XElement.Load(@"..\..\xmlTemplate.xml");
3             XDocument readyMadeXml3 = XDocument.Parse(File.ReadAllText(@"..\..\xmlTemplate.xml"), LoadOptions.PreserveWhitespace);
4             XDocument readyMadeXml4 = XDocument.Load(@"..\..\xmlTemplate.xml", LoadOptions.PreserveWhitespace);

上面的code中會看到一個新的類XDocument ,它的作用類似於XMLDocument,3,4行中會發現第二個參數為LoadOptions,其作用是保留xml一些特定的內容,例如默認情況下在load或者parse後,xml中無意義的空格會被刪除,如果想保留的話就需要在方法中傳入參數LoadOptions.PreserveWhitespace。

4.使用XML命名空間

Linq中與xml的namespace對於的類是XNamespace ,具體的使用方法比傳統的方式更簡潔,示例代碼如下

 1             // The http://www.adventure-works.com namespace is forced to be the default namespace.  
 2             XNamespace aw = "http://www.adventure-works.com";
 3             XNamespace fc = "www.fourthcoffee.com";
 4             XElement root = new XElement(aw + "Root",
 5                 new XAttribute("xmlns", "http://www.adventure-works.com"),
 6                 new XAttribute(XNamespace.Xmlns + "coffee", "www.fourthcoffee.com"),
 7                 new XElement(fc + "Child",
 8                     new XElement(aw + "DifferentChild", "other content"),
 9                     new XElement(aw + "DifferentChild", "hehe")
10                 ),
11                 new XElement(aw + "Child2", "c2 content"),
12                 new XElement(fc + "Child3", "c3 content")
13             );

aw為默認命名空間,沒有前綴,fc有前綴,構造出來的xml如下

技術分享
<Root xmlns="http://www.adventure-works.com" xmlns:coffee="www.fourthcoffee.com">
  <coffee:Child>
    <DifferentChild>other content</DifferentChild>
    <DifferentChild>hehe</DifferentChild>
  </coffee:Child>
  <Child2>c2 content</Child2>
  <coffee:Child3>c3 content</coffee:Child3>
</Root>
View Code

因為xml中包含命名空間,所以對應的在查詢xml的節點時,需要在查詢時在XName中使用+包含XNamespace信息,示例代碼如下

1             IEnumerable<XElement> reslut = from e in root.Elements(fc + "Child").Elements(aw + "DifferentChild")
2                                       select e;
3             foreach (XElement e in reslut)
4             {
5                 Console.WriteLine(e.Value);
6             }

即使默認命名空間不會有前綴,但是在查詢的時候也要需要在節點名前面加默認XNamespace +。

5.檢索查詢修改XML

這部分不給出具體的說明和示例代碼,說多了看過就忘了,自己寫代碼過程中,實際動手最實際,不管是Linq to Object還是Linq to xml,都屬於Linq,所以上篇Linq to Object提到的對於Linq的用法,在Linq to Xml裏也同樣適用。感興趣的可以看下下面的鏈接

https://docs.microsoft.com/zh-cn/dotnet/csharp/programming-guide/concepts/linq/linq-to-xml-axes

https://docs.microsoft.com/zh-cn/dotnet/csharp/programming-guide/concepts/linq/querying-xml-trees

https://docs.microsoft.com/zh-cn/dotnet/csharp/programming-guide/concepts/linq/advanced-query-techniques-linq-to-xml

https://docs.microsoft.com/zh-cn/dotnet/csharp/programming-guide/concepts/linq/modifying-xml-trees-linq-to-xml

這裏特別提一下XPath,XPath在Linq to xml中也同樣的功能,該功能是通過 System.Xml.XPath.Extensions 中的擴展方法實現的,在使用相關方法時需要引用System.Xml.XPath。

5.XML開發中的內存和性能問題

在處理XML的時候需要特別註意內存的使用情況,如果盲目的將這個xml都加載了,如果XML比較小還看不出來影響,如果是有上百萬node的xml的時候內存問題就很嚴重了,那如何控制內存呢,其實很簡單,只把你需要的分析的node使用XElement構造出來,盡可能減少構造XElement的數量,使用流的方式將xml分片段進行處理,下例使用XmlReader流對xml進行分段處理

 1         static void Main(string[] args)
 2         {
 3             IEnumerable<string> wuJiangChild = from w in StreamRootChildDoc(new StringReader(File.ReadAllText(@"..\..\xmlTemplate.xml")))
 4                                                where (int)w.Attribute("XingBie") == 0
 5                                                select w.Value;
 6         }
 7 
 8         static IEnumerable<XElement> StreamRootChildDoc(StringReader stringReader)
 9         {
10             using (XmlReader reader = XmlReader.Create(stringReader))
11             {
12                 reader.MoveToContent();
13                 // Parse the file and display each of the nodes.  
14                 while (reader.Read())
15                 {
16                     switch (reader.NodeType)
17                     {
18                         case XmlNodeType.Element:
19                             if (reader.Name == "WuJiang")
20                             {
21                                 XElement el = XElement.ReadFrom(reader) as XElement;
22                                 if (el != null)
23                                     yield return el;
24                             }
25                             break;
26                     }
27                 }
28             }
29         }

方法StreamRootChildDoc的作用就是從XML中過濾出來需要分析的node,之後構造成XElement集合返回,方法中有一個不常用的關鍵字yield,該關鍵字會向編譯器指出該關鍵字所在的方法是叠代器塊,yield需要分別結合return或者break使用,這裏使用的yield return最大的特點是將XElement“按需生產”出來,而不是一次性都“批量生產”出來供人使用,這麽說可能大家想不明白,對照上面的code看下,在執行第3行的時候,會調用方法StreamRootChildDoc,但是該方法不是一次性執行完的,方法內執行到23行時,會返回一個XElement,而不是XElement集合,接著會繼續執行第4行,如果滿足條件執行第5行,接下來執行的是第14行,如此往復,直到循環完。如果按照傳統的方式,我們可能會在方法StreamRootChildDoc的第一行中定義一個空的集合,之後再23行中把滿足條件的XElement加入到集合中,最後再將集合return,這種方式就是前面提到的“批量生產”。大家可以很簡單的看出來“按需生產”對內存的負擔更小,所以這種方式對於解析大XML的時候能一定程度上的控制內存使用量。

除了上面的內存問題,對於XName的使用也會影響到xml操作的性能,但是不很大,使用XName的時候盡量使用顯示的方式,減少使用隱式的轉換(第一點下的示例代碼中不管是new XElement,還是new XAttribute,采用的都是將字符串隱式的轉換為XName),特別是同樣的XName被反復使用的情況下,同樣的XNamespace使用的時候也符合這種規則。另外,使用 LINQ to XML 中 XPath 功能的 XPath 查詢的執行性能比 LINQ to XML 查詢低,所以對性能有要求的話盡量避免使用XPath。

6.LINQ to XML安全性

該部分暫時未在平時工作中涉及到,此次標記出來,以供以後使用 https://docs.microsoft.com/zh-cn/dotnet/csharp/programming-guide/concepts/linq/linq-to-xml-security

本文參考 https://docs.microsoft.com/zh-cn/dotnet/csharp/programming-guide/concepts/linq/linq-to-xml

Linq學習隨筆二------LINQ to XML