使用節點進行程式設計

必須撰寫程式 (例如,XML 編輯器、轉換系統或報表寫入器) 的 LINQ to XML 開發人員通常需要撰寫的程式可在比項目和屬性更細微的層級上作業。 他們通常需要在節點層級、管理的文字節點、處理指示與註解上作業。 這個主題提供一些關於在節點層級進行程式設計的詳細資料。

節點詳細資料

這裡提供程式設計人員在節點層級上作業時應該知道的一些程式設計詳細資料。

XDocument 之子節點的父屬性設定為 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 does not 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 does not 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 does not 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 does not 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 中,命名空間會當做 XAttribute 物件,儲存在 XML 樹狀結構中。 如果您逐一查看包含命名空間宣告之項目的屬性,您將可以看到命名空間宣告,做為所傳回之集合中的其中一個項目。

IsNamespaceDeclaration 屬性會指出屬性是否為命名空間宣告。

XElement root = XElement.Parse(
@"<Root
    xmlns='https://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='https://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="https://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

XDeclaration 物件不是節點

當您逐一查看 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

請參閱

概念

LINQ to XML 進階程式設計