程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> C# >> C#入門知識 >> C#學習之Linq to Xml

C#學習之Linq to Xml

編輯:C#入門知識

前言   我相信很多從事.NET開發的,在.NET 3.5之前操作XML會比較麻煩,但是在此之後出現了Linq to Xml,而今天的主人公就是Linq to Xml,廢話不多說,直接進入主題。   題外:最近由於身體原因去醫院,耽誤了不少時間,不然這篇隨筆可能早就完成了。       實例項目下載        目錄:    生成xml   創建簡單的xml   創建注釋   根據對象創建xml   創建屬性   創建命名空間   查詢並修改xml   通過文件讀取xml   在指定節點前後添加新節點   添加屬性到節點中   添加注釋到指定節點前後   替換指定節點   刪除指定屬性   刪除指定節點    按節點關系查詢   顯示指定節點的所有父節點   顯示指定節點的所有子節點   顯示同級節點之前的節點   顯示同級節點之後的節點   監聽xml事件   處理xml流       一、生成Xml   為了能夠在結構有一定的組織,筆者建議大家新建一個控制台項目,並且新建一個CreateXml類(以下部分都屬於該類中)。   並在其中寫入以下屬性:   復制代碼 1         public static String Path 2         { 3             get 4             { 5                 String path = String.Format("{0}\\test.xml", Environment.CurrentDirectory); 6                 return path; 7             } 8         } 復制代碼         這句代碼很好理解,就是為了下面我們示例的時候可以將xml保存到當前程序的運行路徑下。   (以下的示例中不會包含Main方法中的寫法,因為Main中僅僅只要調用該靜態方法即可。)       1.創建簡單的Xml   首先我們先練練手,創建一個簡單的Xml並保存到一個文件中。   代碼如下:   復制代碼  1 /// <summary>  2 /// 創建簡單的xml並保存  3 /// </summary>  4 public static void CreateElement()  5 {  6 XDocument xdoc = new XDocument(  7 new XDeclaration("1.0", "utf-8", "yes"),  8 new XElement("root",  9 new XElement("item", "1"), 10 new XElement("item", "2") 11 )); 12 xdoc.Save(Path); 13 } 復制代碼         很多學習過XML的人可以從結構就能夠猜測出最終的xml的組織,而這也是linq to xml的優點之一。這句代碼首先創建一個xml文檔,並設置該xml的版本為1.0,   采用utf-8編碼,後面的yes表示該xml是獨立的。下面就開始創建每個節點的,首先是Root節點,然後在Root節點中添加兩個Item節點。       最終生成的Xml如下所示:   1 <?xml version="1.0" encoding="utf-8" standalone="yes"?> 2 <root> 3   <item>1</item> 4   <item>2</item> 5 </root>         2.創建注釋   當xml有很多項時,我們就需要利用注釋加以區別,通過linq to xml我們一樣可以在其中添加注釋。       比如下面這段代碼:   復制代碼  1         /// <summary>  2         /// 創建注釋  3         /// </summary>  4         public static void CreateComment()  5         {  6             XDocument doc = new XDocument(  7                 new XDeclaration("1.0", "utf-8", "yes"),  8                 new XComment("提示"),  9                 new XElement("item", "asd") 10                 ); 11             doc.Save(Path); 12         } 復制代碼         這裡我們直接在版本信息的後面添加了一條注釋。       最終的結果如下所示:   1 <?xml version="1.0" encoding="utf-8" standalone="yes"?> 2 <!--提示--> 3 <item>asd</item>         3.根據對象創建xml   很多時候我們都會將數組之類的類型轉換成xml以便保存進永久性存儲介質中,所以下面我們也簡單的舉了一個例子,將數組轉換成xml。       代碼如下所示:   復制代碼  1         /// <summary>  2         /// 根據對象創建xml並保存  3         /// </summary>  4         public static void CreateElementByObjects()  5         {  6             var s = Enumerable.Range(1, 10);  7             XElement xele = new XElement(  8                 "Root",  9                 from item in s 10                 select new XElement("item", item.ToString()) 11                 ); 12             xele.Save(Path); 13         } 復制代碼         一開始的代碼 var s = Enumerable.Radge(1,10)是從1開始遞增,生成含有10項的數組,以便後面我們進行添加,有了這個數組之後,   我們通過簡單的linq語句將數組轉換成xml,添加到Root中。       保存之後的結果如下:   復制代碼  1 <?xml version="1.0" encoding="utf-8"?>  2 <Root>  3   <item>1</item>  4   <item>2</item>  5   <item>3</item>  6   <item>4</item>  7   <item>5</item>  8   <item>6</item>  9   <item>7</item> 10   <item>8</item> 11   <item>9</item> 12   <item>10</item> 13 </Root> 復制代碼         4.創建屬性   有時我們不想創建新的子項去保存數據,而是使用屬性的方式去保存。理所應當,linq to xml一樣也支持這個功能,下面我們可以通過簡單的語句去實現它。       代碼如下所示:   復制代碼  1         /// <summary>  2         /// 創建屬性  3         /// </summary>  4         public static void CreteAttribute()  5         {  6             XAttribute xa = new XAttribute("V2", "2");  7             XElement xele = new XElement(  8                 "Root",  9                 new XElement("Item", 10                     new XAttribute("V1", "1"), 11                     xa 12                     )); 13             xele.Save(Path); 14         } 復制代碼         我們依然可以看到熟悉的語法,這裡我們利用了XAttribute去創建一個屬性,並添加到XElement中。       最終的結果如下:   1 <?xml version="1.0" encoding="utf-8"?> 2 <Root> 3   <Item V1="1" V2="2" /> 4 </Root>         5.創建命名空間   對於一些企業級的xml格式,會非常的嚴格。特別是在同一個xml中可能會出現重復的項,但是我們又想區分開來,這個時候我們可以利用命名空間將他們分開(跟C#中的命名空間類似。)。       下面是創建命名空間的示例:   復制代碼  1         /// <summary>  2         /// 創建命名空間  3         /// </summary>  4         public static void CreateNamespace()  5         {  6             XElement xele = new XElement("{http://www.xamarin-cn.com}Root",  7                 new XElement("Item", "1"),  8                 new XElement("{http://www.baidu.com}Item", 2));  9             xele.Save(Path); 10         } 復制代碼         結果如下所示:   1 <?xml version="1.0" encoding="utf-8"?> 2 <Root xmlns="http://www.xamarin-cn.com"> 3   <Item xmlns="">1</Item> 4   <Item xmlns="http://www.baidu.com">2</Item> 5 </Root>         從這個結果中我們可以看到對應的屬性中有了xmlns屬性,並且值就是我們賦給它的命名空間。       二、查詢並修改Xml   Linq to xml不僅僅是創建xml簡單,在查詢,編輯和刪除方面一樣是非常方便的。下面我們就會介紹這些。   首先我們創建一個QueryXml類,並在其中寫入如下的屬性:   復制代碼 1         public static String Path 2         { 3             get 4             { 5                 String path = String.Format("{0}\\test1.xml", Environment.CurrentDirectory); 6                 return path; 7             } 8         } 復制代碼         同時在該路徑下新建一個test1.xml文件,並在其中寫入如下內容:   1 <?xml version="1.0" encoding="utf-8"?> 2 <Root> 3   <Item v1="1" v2="2">Item1</Item> 4   <Item v1="1" v2="2" >Item2</Item> 5 </Root>     下面我們就可以正式開始了。       1.通過文件讀取xml   既然我們要對xml查詢就需要讀取對應的xml文件,當然後面會介紹其他的方式。       代碼如下:   復制代碼  1         /// <summary>  2         /// 通過文件讀取xml  3         /// </summary>  4         public static void QueryElementByFile()  5         {  6             XElement xele = XElement.Load(Path);  7             XElement xele1 = xele.Element("Item");  8             Console.Write(xele1.Value.Trim());  9             Console.ReadKey(); 10         } 復制代碼         我們可以利用XElement的靜態方法Load讀取指定路徑下的xml文件,這裡我們不僅讀取了該xml文件,同時還獲取的該xml的第一個item的值並輸出。       所以我們可以看到如下的結果:           2.在指定節點前後添加新節點   上面我們僅僅只是讀取xml以及簡單的查詢,下面我們不僅僅查詢並且還要在該節點前後插入新的節點。       代碼如下:   復制代碼  1         /// <summary>  2         /// 在指定節點前後添加新節點  3         /// </summary>  4         public static void AddToElementAfterAndBefore()  5         {  6             XElement xele = XElement.Load(Path);  7             var item = (from ele in xele.Elements("Item")  8                         where ele.Value.Equals("Item2")  9                         select ele).SingleOrDefault(); 10             if (item != null) 11             { 12                 XElement nele = new XElement("NItem", "NItem"); 13                 XElement nele2 = new XElement("BItem", "BItem"); 14                 item.AddAfterSelf(nele); 15                 item.AddBeforeSelf(nele2); 16                 xele.Save(Path); 17             } 18         } 復制代碼         我們簡單的分析一下上面的代碼,首先我們利用linq從中查詢Item的值為Item2的節點,然後獲取其中第一個節點,然後通過AddAfterSelf和AddBeforeSelf在該節點的後面和前面分別添加新的節點。       添加完之後的xml結果如下:   復制代碼 1 <?xml version="1.0" encoding="utf-8"?> 2 <Root> 3   <Item v1="1" v2="2">Item1</Item> 4   <BItem>BItem</BItem> 5   <Item v1="1" v2="2">Item2</Item> 6   <NItem>NItem</NItem> 7 </Root> 復制代碼         3.添加屬性到節點中   我們已經可以動態的添加節點,但是創建的時候不僅僅可以創建節點,並且還能創建屬性,下面我們可以通過SetAttributeValue去添加新的屬性或者修改現有屬性。       代碼如下:   復制代碼  1         /// <summary>  2         /// 添加屬性到節點中  3         /// </summary>  4         public static void AddAttributeToEle()  5         {  6             XElement xele = XElement.Parse(@"<?xml version='1.0' encoding='utf-8'?><Root><!--前面的注釋-->  7 <Item v1='1' v2='2'>Item1</Item><!--後面的注釋--><Item v1='1' v2='2' v3='3'>Item2</Item></Root>");  8             var item = (from ele in xele.Elements("Item")  9                         where ele.Value.Equals("Item2") 10                         select ele).SingleOrDefault(); 11             item.SetAttributeValue("v3", "3"); 12             xele.Save(Path); 13         } 復制代碼         我們可以明顯的看出,這裡我們已經不是使用XElement.Load去讀取xml文件,而是通過直接讀取xml字符串。接著我們還是跟上面一樣去查詢,然後通過SetAttributeValue添加了新的屬性,並保存。       Xml內容如下:   復制代碼 1 <?xml version="1.0" encoding="utf-8"?> 2 <Root> 3   <!--前面的注釋--> 4   <Item v1="1" v2="2">Item1</Item> 5   <!--後面的注釋--> 6   <Item v1="1" v2="2" v3="3">Item2</Item> 7 </Root> 復制代碼         我們可以看到第二個Item中多了一個 v3=”3” 新的屬性。       4.添加注釋到指定節點前後   這裡的語法基本跟添加節點到指定節點前後是相似的,只是讀取xml的方式不同。       代碼如下:   復制代碼  1         /// <summary>  2         /// 添加注釋到節點前後  3         /// </summary>  4         public static void AddCommentToAfterAndBefore()  5         {  6             TextReader tr = new StringReader(@"<?xml version='1.0' encoding='utf-8'?><Root><!--前面的注釋-->  7 <Item v1='1' v2='2'>Item1</Item><!--後面的注釋--><Item v1='1' v2='2' v3='3'>Item2</Item></Root>");  8             XElement xele = XElement.Load(tr);  9             var item = (from ele in xele.Elements("Item") 10                         where ele.Value.Equals("Item1") 11                         select ele).FirstOrDefault(); 12             if (item != null) 13             { 14                 XComment xcom = new XComment("後面的注釋"); 15                 XComment xcoma = new XComment("前面的注釋"); 16                 item.AddAfterSelf(xcom); 17                 item.AddBeforeSelf(xcoma); 18             } 19             tr.Close(); 20             xele.Save(Path); 21         } 復制代碼         上面我使用StringReader和TextReader讀取xml字符串並使用XElement.Load讀取該對象,然後就是在新建節點的時候新建的是注釋節點,最後利用一樣的語法添加到指定節點前後。       最終結果如下:   復制代碼 1 <?xml version="1.0" encoding="utf-8"?> 2 <Root> 3   <!--前面的注釋--> 4   <!--前面的注釋--> 5   <Item v1="1" v2="2">Item1</Item> 6   <!--後面的注釋--> 7   <!--後面的注釋--> 8   <Item v1="1" v2="2" v3="3">Item2</Item> 9 </Root> 復制代碼         5.替換指定節點   修改節點的值通過SetValue即可做到,但是有時涉及到子節點,而我們想一次性全部替換掉,那麼我們就需要使用ReplaceWith。       代碼如下:   復制代碼  1         /// <summary>  2         /// 替換指定節點  3         /// </summary>  4         public static void ReplaceElement()  5         {  6             XElement xele = XElement.Load(Path);  7             var item = (from ele in xele.Elements("Item")  8                         where ele.Value.Equals("Item2")  9                         select ele).FirstOrDefault(); 10             if (item != null) 11             { 12                 item.ReplaceWith(new XElement("Item", "Item3")); 13             } 14             xele.Save(Path); 15         } 復制代碼         這裡的重點在於ReplaceWith方法,調用該方法會發生兩個操作。首先是刪除該節點,然後在該節點的位置上將我們的節點插入完成替換。       最後的xml結果如下:   復制代碼 1 <?xml version="1.0" encoding="utf-8"?> 2 <Root> 3   <!--前面的注釋--> 4   <!--前面的注釋--> 5   <Item v1="1" v2="2">Item1</Item> 6   <!--後面的注釋--> 7   <!--後面的注釋--> 8   <Item>Item3</Item> 9 </Root> 復制代碼         這樣我們很輕易的就替換了整個節點。       6.刪除指定屬性   前面我們介紹了創建、修改和添加屬性,但是還沒有介紹如何刪除指定的屬性,下面我們就通過一個簡單的實例來演示。       代碼如下:   復制代碼  1         /// <summary>  2         /// 刪除指定屬性  3         /// </summary>  4         public static void RemoveAttribute()  5         {  6             XElement xele = XElement.Load(Path);  7             var item = (from ele in xele.Elements("Item")  8                         where ele.Value.Equals("Item1")  9                         select ele).FirstOrDefault().Attribute("v1"); 10             if (item != null) 11             { 12                 item.Remove(); 13             } 14             xele.Save(Path); 15         } 復制代碼         我們首先查詢出指定的節點,然後指定某個屬性,最後調用XAttribute的Remove方法既可。       結果如下:   復制代碼 1 <?xml version="1.0" encoding="utf-8"?> 2 <Root> 3   <!--前面的注釋--> 4   <!--前面的注釋--> 5   <Item v2="2">Item1</Item> 6   <!--後面的注釋--> 7   <!--後面的注釋--> 8   <Item>Item3</Item> 9 </Root> 復制代碼         7.刪除指定節點   既然上面已經可以刪除屬性,自然也少不了刪除屬性。       代碼如下所示:   復制代碼  1         /// <summary>  2         /// 刪除指定節點  3         /// </summary>  4         public static void RemoveElement()  5         {  6             XElement xele = XElement.Load(Path);  7             var item = (from ele in xele.Elements("Item")  8                         where ele.Value.Equals("Item1")  9                         select ele).FirstOrDefault(); 10             if (item != null) 11             { 12                 item.Remove(); 13             } 14             xele.Save(Path); 15         } 復制代碼         依然是調用同樣的方法。       結果如下:   復制代碼 1 <?xml version="1.0" encoding="utf-8"?> 2 <Root> 3   <!--前面的注釋--> 4   <!--前面的注釋--> 5   <!--後面的注釋--> 6   <!--後面的注釋--> 7   <Item>Item3</Item> 8 </Root> 復制代碼         三、按節點關系查詢   上面的查詢都是通過相關的條件進行查詢,但是我們有時僅僅只需要通過之間的關系即可,這樣反而可以避免很多的代碼,當然稍加探索可以發現其實XElement都提供給我們了。   我們依然要新建一個StructureXml類,並在其中新建一個屬性。       如下所示:   復制代碼 1         public static String Path 2         { 3             get 4             { 5                 String path = String.Format("{0}\\test2.xml", Environment.CurrentDirectory); 6                 return path; 7             } 8         } 復制代碼         同時在該文件夾下新建一個test2.xml並寫入如下內容:   復制代碼  1 <?xml version="1.0" encoding="utf-8" ?>  2 <Root>  3   <Item>  4     <SubItem1>  5       1  6     </SubItem1>  7     <SubItem>  8       <Child>  9         sss 10       </Child> 11     </SubItem> 12     <SubItem2> 13       2 14     </SubItem2> 15   </Item> 16 </Root> 復制代碼         1.顯示指定節點的所有父節點   通過上面的xml文件,我們清晰的看出xml是具有結構性的,彼此之間都存在關系,而現在我們需要顯示某個節點的父級節點的名稱。       代碼如下所示:   復制代碼  1         /// <summary>  2         /// 顯示指定節點的所有父節點  3         /// </summary>  4         public static void ShowAllParentEle()  5         {  6             XElement xele = XElement.Load(Path);  7             var item = (from ele in xele.Descendants("Child")  8                         select ele).FirstOrDefault();  9             if (item != null) 10             { 11                 foreach (var sub in item.Ancestors()) 12                 { 13                     Console.WriteLine(sub.Name); 14                 } 15                 Console.WriteLine("----------------"); 16                 foreach (var sub in item.AncestorsAndSelf()) 17                 { 18                     Console.WriteLine(sub.Name); 19                 } 20                 Console.ReadKey(); 21             } 22         } 復制代碼         其中我們通過Descendants獲取最底的節點,然後使用Ancestors獲取所有的父級節點,而AncestorsAndSelf則表示包含本身。       最終結果如下所示:           我們從圖中看出,分割線前顯示的是不包含本身的,而下面是包含本身的。       2.顯示指定節點的所有子節點   我們不僅僅可以輸出一個節點的所有父級節點,同樣也可以輸出一個節點的所有子節點。       代碼如下所示:   復制代碼  1         /// <summary>  2         /// 顯示指定節點的所有子節點  3         /// </summary>  4         public static void ShowAllChildEle()  5         {  6             XElement xele = XElement.Load(Path);  7             foreach (var sub in xele.Descendants())  8             {  9                 Console.WriteLine(sub.Name); 10             } 11             Console.WriteLine("-----------------"); 12             foreach (var sub in xele.DescendantsAndSelf()) 13             { 14                 Console.WriteLine(sub.Name); 15             } 16             Console.ReadKey(); 17         } 復制代碼         這裡我們依然是分成輸出子級節點以及包含自己的。       結果如下所示:           3.顯示同級節點之前的節點   既然有了父子關系,當然也少不了同級關系,首先我們先顯示同級節點之前的節點。       代碼如下所示:   復制代碼  1         /// <summary>  2         /// 顯示同級節點之前的節點  3         /// </summary>  4         public static void ShowPrevEle()  5         {  6             XElement xele = XElement.Load(Path);  7             var item = (from ele in xele.Descendants("SubItem")  8                         select ele).FirstOrDefault();  9             if (item != null) 10             { 11                 foreach (var sub in item.ElementsBeforeSelf()) 12                 { 13                     Console.WriteLine(sub.Name); 14                 } 15             } 16             Console.ReadKey(); 17         } 復制代碼         這裡我們看到我們通過ElementsBeforeSelf獲取該節點之前的同級節點,當然我們還可以傳入參數作為限制條件。這裡我們通過查詢獲取了SubItem這個節點,並顯示該節點之前的同級節點。       最終結果如下:           4.顯示同級節點後面的節點   作為上面的補充。       代碼如下所示:   復制代碼  1        /// <summary>  2         /// 顯示同級節點後面的節點  3         /// </summary>  4         public static void ShowNextEle()  5         {  6             XElement xele = XElement.Load(Path);  7             var item = (from ele in xele.Descendants("SubItem")  8                         select ele).FirstOrDefault();  9             if (item != null) 10             { 11                 foreach (var sub in item.ElementsAfterSelf()) 12                 { 13                     Console.WriteLine(sub.Name); 14                 } 15             } 16             Console.ReadKey(); 17         } 復制代碼         最終結果如下所示:           四、監聽xml事件   你可能會疑惑xml為什麼還要監聽,其實這樣是有意義的,比如你要根據某個節點的值作為依賴,那麼你就要監聽這個節點,如果這個節點發生改變的時候,   你才可以及時的作出反應。但是xml的事件監聽有一個特點,跟浏覽器中的DOM事件類似,監聽父節點同樣也可以監聽的到它的子節點的事件。下面我們   通過一個簡單的實例來說明。       實例代碼如下:   復制代碼  1     public static class EventXml  2     {  3         public static void BindChangeing()  4         {  5             XElement xele = new XElement("Root");  6             xele.Changing += xele_Changing;  7             xele.Changed += xele_Changed;  8             xele.Add(new XElement("Item", "123"));  9             var item = xele.Element("Item"); 10             item.ReplaceWith(new XElement("Item", "2")); 11             item = xele.Element("Item"); 12             item.Remove(); 13             Console.ReadKey(); 14         } 15  16         static void xele_Changed(object sender, XObjectChangeEventArgs e) 17         { 18             XElement ele = sender as XElement; 19             Console.WriteLine(String.Format("已完成 {0}-{1}", ele.Name, e.ObjectChange)); 20         } 21  22         static void xele_Changing(object sender, XObjectChangeEventArgs e) 23         { 24             XElement ele = sender as XElement; 25             Console.WriteLine(String.Format("正在進行中 {0}-{1}", ele.Name, e.ObjectChange)); 26         } 27 } 復制代碼         其中的關鍵就是Changing和Changed事件,其次就是在事件中判斷事件的來源。       最終結果如下所示:           五、處理xml流   在實際的商業化的開發中,xml不可能僅僅保存這麼點數據。有可能保存著非常多的數據。但是我們還是按照以往的方式,就會將xml全部讀取進內存。   這樣會占據很多內存,影響系統的性能,針對這種情況我們需要使用流的方式去處理xml,因為流會按照我們的順序讀取部分xml進內存,並不會將所   有xml都讀取進內存。       Xml文件內容如下所示:   復制代碼 1 <?xml version="1.0" encoding="utf-8" ?> 2 <Root> 3   <SubItem>1</SubItem> 4   <SubItem>1</SubItem> 5   <SubItem>1</SubItem> 6   <Item>A</Item> 7   <SubItem>1</SubItem> 8   <Item>B</Item> 9 </Root> 復制代碼         代碼如下所示:   復制代碼  1     public static class ReadXmlStream  2     {  3         public static String Path  4         {  5             get  6             {  7                 String path = String.Format("{0}\\test3.xml", Environment.CurrentDirectory);  8                 return path;  9             } 10         } 11  12         /// <summary> 13         /// 流式處理XML 14         /// </summary> 15         public static void ReadXml() 16         { 17             XmlReader reader = XmlReader.Create(Path); 18             while (reader.Read()) 19             { 20                 if (reader.NodeType == XmlNodeType.Element && reader.Name.Equals("Item")) 21                 { 22                     XElement ele = XElement.ReadFrom(reader) as XElement; 23                     Console.WriteLine(ele.Value.Trim()); 24                 } 25             } 26             Console.ReadKey(); 27         } 28 }

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved