導航/查詢X-DOM
就像你可能期望的那樣, XNode和XContainer類定義了方法和屬性來測量X-DOM樹. 與常規的DOM不同的是, 這些函數並不會返回實現了IList<T>的集合, 而是返回了一個單一值或者一個實現了IEnumerable<T>的序列. 基於這個你可以執行一個LINQ查詢或者使用foreach來做枚舉. 這同時也允許你使用熟悉的LINQ查詢語法來執行簡單的導航任務或者高級查詢.
在X-DOM中, 元素和屬性名都是大小寫敏感的, 這與XML是一致的.
FirstNode, LastNode與Nodes
FirstNode與LastNode允許你直接訪問第一個或者最後一個子節點; Nodes返回所有的子節點並形成一個序列. 這三個方法只用於直系的後代節點.
檢索元素
Elements方法返回類型為XElement的子節點. 例如:
1: var bench = new XElement ("bench",
2: new XElement ("toolbox",
3: new XElement ("handtool", "Hammer"),
4: new XElement ("handtool", "RASP")
5: ),
6: new XElement ("toolbox",
7: new XElement ("handtool", "Saw"),
8: new XElement ("powertool", "Nailgun")
9: ),
10: new XComment ("Careful with the nailgun")
11: );
12:
13: foreach (XElement e in bench.Elements( ))
14: Console.WriteLine (e.Name + "=" + e.Value);
15:
16: // 結果: toolbox=HammerRASP
17: toolbox=SawNailgun
以下的LINQ查詢用於查詢包含nail gun的toolbox:
1: IEnumerable<string> query =
2: from toolbox in bench.Elements( )
3: where toolbox.Elements( ).Any
4: (tool => tool.Value == "Nailgun")
5: select toolbox.Value;
6:
7: RESULT: { "SawNailgun" }
Elements等價於Nodes上面的LINQ查詢, 我們之前的查詢也可以被寫為:
1: from toolbox in bench.Nodes().OfType<XElement>()
2: where ...
接下來的例子使用一個SelectMany查詢檢索hand tools:
1: IEnumerable<string> query =
2: from toolbox in bench.Elements( )
3: from tool in toolbox.Elements( )
4: where tool.Name == "handtool"
5: select tool.Value;
6:
7: RESULT: { "Hammer", "RASP", "Saw" }
Elements也可以只返回給定名稱的元素, 例如:
1: int x = bench.Elements ("toolbox").Count(); // 2
這等價於:
1: int x = bench.Elements()
2: .Where (e => e.Name == "toolbox")
3: .Count(); // 2
4:
Elements還定義了一個擴展方法接受IEnumerable<XContainer>參數. 更精確的說, 它接受了此種類型的參數:
1: IEnumerable<T> where T : XContainer
這讓其可以和元素序列一起工作, 使用這個方法我們可以重寫查找hand tools的查詢:
1: from tool in bench.Elements ("toolbox")
2: .Elements ("handtool")
3: select tool.Value.ToUpper( );
第一個Elements綁定到XContainer上, 第二個則綁定到擴展方法上.
讀取一個單一的元素
方法Element(單數)返回匹配給定名稱的第一個元素. Element對於簡單的導航是非常有用的, 例如:
1: var settings = XElement.Load ("databaseSettings.XML");
2:
3: string cx = settings.Element ("database")
4: .Element ("connectString")
5: .Value;
Element的作用相當於調用Elements然後再應用LINQ的FirstOrDefault並給定一個名稱作為匹配斷言. 如果沒有任何元素匹配到, 則Element返回null.
如果元素xyz不存在, 那麼Element(”xyz”).Value將會拋出一個NullReferenceException異常. 如果你傾向於使用null代替異常, 可以將XElement轉換成string而不是調用它的Value屬性, 如下:
1: string xyz =
2: (string) settings.Element("xyz");
XElement定義了一個顯示的string轉換正式為了這個目的.
遞歸功能
XContainer同時也定義了Descendants和DescendantNodes方法, 它們遞歸地返回子元素或者子節點.Descendant接受一個可選的元素名, 會到我們之前的例子, 我們可以使用Descendants去查找所有的hand tools:
1: Console.WriteLine
2: (bench.Descendants ("handtool").Count( )); // 3
不管是父節點還是葉節點都包含在整體橫切中. 以下的查詢取得所有包含單詞”careful”, 存在於X-DOM任何地方的注釋節點:
1: IEnumerable<string> query =
2: from c in bench.DescendantNodes( ).OfType<XComment>( )
3: where c.Value.Contains ("careful")
4: orderby c.Value
5: select c.Value;
查詢父節點
所有的XNodes都有一個Parent屬性和AncestorXX的方法用於父節點導航. 一個父親節點永遠是一個XElement.
如果x是一個XElement, 以下代碼打印true:
1: foreach (XNode child in x.Nodes( ))
2: Console.WriteLine (child.Parent == x);
如果x是一個XDocument的話, XDocument有點獨特, 它永遠不能作為任何節點的父親節點. 為了訪問XDocument,應該使用Document屬性, 這只爭對X-DOM樹上的任意對象可以工作.
Ancestors返回一個序列其第一個元素是Parent, 下一個元素則是Parent.Parent, 依次類推直到根元素.
你還可以使用LINQ查詢AncestorsAndSelf().Last()來取得輸入序列的根元素. 另外一種方法是調用Document.Root, 但只有當XDocument呈現的時候才能工作.
對等節點導航
使用PreviousNode和NextNode(FirstNode / LastNode),我們可以使用一個linked list橫貫所有的節點. 這並不是巧合, 在內部, Nodes實際上就是存儲在一個linked list.
屬性導航
XAttributes定義了PreviousAttribute和NextAttribute, Parent也是一樣.
Attributes方法接受了一個名稱並返回包含0或1個元素的序列; 在XML中, 一個元素不能包含重復的屬性名.
待續!