ノードでのプログラミング

更新 : November 2007

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 プログラミング モデルでは、隣接するテキスト ノードが常に連結されます。これをテキスト ノードの正規化と呼ぶことがありますが、LINQ to XML ではテキスト ノードを正規化しません。同じ要素に 2 つのテキスト ノードを追加すると、隣接するテキスト ノードになります。ただし、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 ツリーに格納されます。名前空間宣言を含んでいる要素の属性を反復処理すると、名前空間宣言が、返されるコレクションの項目の 1 つになります。

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 プログラミング