使用節點進行程式設計 (LINQ to XML)

必須撰寫 XML 編輯器、轉換系統或報表寫入器等程式的 LINQ to XML 開發人員通常需要可在比元素與屬性更細微的層級上作業的程式碼。 他們通常需要在節點層級、管理的文字節點、處理指示與處理註解上作業。 本文提供有關節點層級程式設計的資訊。

範例:XDocument 子節點的 Parent 屬性值會設定為 null

Parent 屬性包含父代 XElement,而不包含父節點。 XDocument 的子節點沒有父代 XElement。 其父代為文件,因此,這些節點的 Parent 屬性會設定為 null

下列範例為其示範:

XDocument doc = XDocument.Parse(@"<!-- a comment --><Root/>");
Console.WriteLine(doc.Nodes().OfType<XComment>().First().Parent == null);
Console.WriteLine(doc.Root.Parent == null);
Dim doc As XDocument = XDocument.Parse("<!-- a comment --><Root/>")
Console.WriteLine(doc.Nodes().OfType(Of XComment).First().Parent Is Nothing)
Console.WriteLine(doc.Root.Parent Is Nothing)

這個範例會產生下列輸出:

True
True

範例:新增文字可能或可能不會建立新的文字節點

在多個 XML 程式撰寫模型 (Programming Model) 中,相鄰的文字節點永遠會遭到合併。 這有時候稱為文字節點的正規化。 LINQ to XML 不會將文字節點標準化。 如果您將兩個文字節點加入到相同的項目,則會讓兩個文字節點變成相鄰的。 然而,若您新增指定為字串 (而非 XText 節點) 的內容,LINQ to XML 可能會合併字串與相鄰的文字節點。 下列範例示範此作業。

XElement xmlTree = new XElement("Root", "Content");

Console.WriteLine(xmlTree.Nodes().OfType<XText>().Count());

// this doesn't add a new text node
xmlTree.Add("new content");
Console.WriteLine(xmlTree.Nodes().OfType<XText>().Count());

// this does add a new, adjacent text node
xmlTree.Add(new XText("more text"));
Console.WriteLine(xmlTree.Nodes().OfType<XText>().Count());
Dim xmlTree As XElement = <Root>Content</Root>
Console.WriteLine(xmlTree.Nodes().OfType(Of XText)().Count())

' This doesn't add a new text node.
xmlTree.Add("new content")
Console.WriteLine(xmlTree.Nodes().OfType(Of XText)().Count())

'// This does add a new, adjacent text node.
xmlTree.Add(New XText("more text"))
Console.WriteLine(xmlTree.Nodes().OfType(Of XText)().Count())

這個範例會產生下列輸出:

1
1
2

範例:將文字節點值設定為空字串並不會刪除節點

在某些 XML 程式撰寫模型中,保證文字節點不包含空字串。 原因是,這種文字節點對於序列化 XML 不會有任何影響。 然而,因為相同的原因,文字節點可以為相鄰,若您藉由將文字節點值設定為空字串來移除該文字節點中的文字,則文字節點本身將不會遭到刪除。

XElement xmlTree = new XElement("Root", "Content");
XText textNode = xmlTree.Nodes().OfType<XText>().First();

// the following line doesn't cause the removal of the text node.
textNode.Value = "";

XText textNode2 = xmlTree.Nodes().OfType<XText>().First();
Console.WriteLine(">>{0}<<", textNode2);
Dim xmlTree As XElement = <Root>Content</Root>
Dim textNode As XText = xmlTree.Nodes().OfType(Of XText)().First()

' The following line doesn't cause the removal of the text node.
textNode.Value = ""

Dim textNode2 As XText = xmlTree.Nodes().OfType(Of XText)().First()
Console.WriteLine(">>{0}<<", textNode2)

這個範例會產生下列輸出:

>><<

範例:具有一個空白文字節點之元素的序列化與沒有文字節點的元素不同

若元素僅包含一個空的文字子節點,則該元素會以下列長標籤語法序列化:<Child></Child>。 若元素不包含任何子節點,則該元素會以下列短標籤語法序列化:<Child />

XElement child1 = new XElement("Child1",
    new XText("")
);
XElement child2 = new XElement("Child2");
Console.WriteLine(child1);
Console.WriteLine(child2);
Dim child1 As XElement = New XElement("Child1", _
    New XText("") _
)
Dim child2 As XElement = New XElement("Child2")
Console.WriteLine(child1)
Console.WriteLine(child2)

這個範例會產生下列輸出:

<Child1></Child1>
<Child2 />

範例:命名空間為 LINQ to XML 樹狀結構中的屬性

即使命名空間宣告與屬性具有相同的語法,在某些程式設計介面 (例如,XSLT 與 XPath) 中,不會將命名空間宣告視為屬性。 然而,在 LINQ to XML 中,命名空間會儲存為 XML 樹狀結構中的 XAttribute 物件。 若您逐一查看包含命名空間宣告之元素的屬性,將可以看到命名空間宣告,作為所傳回集合中的其中一個元素。 IsNamespaceDeclaration 屬性會指出屬性是否為命名空間宣告。

XElement root = XElement.Parse(
@"<Root
    xmlns='http://www.adventure-works.com'
    xmlns:fc='www.fourthcoffee.com'
    AnAttribute='abc'/>");
foreach (XAttribute att in root.Attributes())
    Console.WriteLine("{0}  IsNamespaceDeclaration:{1}", att, att.IsNamespaceDeclaration);
Dim root As XElement = _
<Root
    xmlns='http://www.adventure-works.com'
    xmlns:fc='www.fourthcoffee.com'
    AnAttribute='abc'/>
For Each att As XAttribute In root.Attributes()
    Console.WriteLine("{0}  IsNamespaceDeclaration:{1}", att, _
                      att.IsNamespaceDeclaration)
Next

這個範例會產生下列輸出:

xmlns="http://www.adventure-works.com"  IsNamespaceDeclaration:True
xmlns:fc="www.fourthcoffee.com"  IsNamespaceDeclaration:True
AnAttribute="abc"  IsNamespaceDeclaration:False

範例:XPath 軸方法不會傳回 XDocument 的子文字節點

只要文字節點只包含空白字元,LINQ to XML 就允許使用 XDocument 的文字子節點。 然而,XPath 物件模型不包含作為文件子節點的空白字元,因此,當您使用 Nodes 座標軸逐一查看 XDocument 的子系時,將不會傳回空白文字節點。 再來,當您使用 XPath 座標軸方法逐一查看 XDocument 的子系時,將不會傳回空白文字節點。

// Create a document with some white space child nodes of the document.
XDocument root = XDocument.Parse(
@"<?xml version='1.0' encoding='utf-8' standalone='yes'?>

<Root/>

<!--a comment-->
", LoadOptions.PreserveWhitespace);

// count the white space child nodes using LINQ to XML
Console.WriteLine(root.Nodes().OfType<XText>().Count());

// count the white space child nodes using XPathEvaluate
Console.WriteLine(((IEnumerable)root.XPathEvaluate("text()")).OfType<XText>().Count());
' Create a document with some white space child nodes of the document.
Dim root As XDocument = XDocument.Parse( _
"<?xml version='1.0' encoding='utf-8' standalone='yes'?>" & _
vbNewLine & "<Root/>" & vbNewLine & "<!--a comment-->" & vbNewLine, _
LoadOptions.PreserveWhitespace)

' Count the white space child nodes using LINQ to XML.
Console.WriteLine(root.Nodes().OfType(Of XText)().Count())

' Count the white space child nodes using XPathEvaluate.
Dim nodes As IEnumerable = CType(root.XPathEvaluate("text()"), IEnumerable)
Console.WriteLine(nodes.OfType(Of XText)().Count())

這個範例會產生下列輸出:

3
0

XDocument 的 XML 宣告節點是屬性,而非子節點

當您逐一查看 XDocument 的子節點時,將不會看到 XML 宣告物件。 這是文件的屬性,而不是其子節點。

XDocument doc = new XDocument(
    new XDeclaration("1.0", "utf-8", "yes"),
    new XElement("Root")
);
doc.Save("Temp.xml");
Console.WriteLine(File.ReadAllText("Temp.xml"));

// this shows that there is only one child node of the document
Console.WriteLine(doc.Nodes().Count());
Dim doc As XDocument = _
<?xml version='1.0' encoding='utf-8' standalone='yes'?>
<Root/>

doc.Save("Temp.xml")
Console.WriteLine(File.ReadAllText("Temp.xml"))

' This shows that there is only one child node of the document.
Console.WriteLine(doc.Nodes().Count())

這個範例會產生下列輸出:

<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<Root />
1