ノードを使用したプログラミング (LINQ to XML)

XML エディター、変換システム、レポート作成プログラムなどのプログラムを作成する LINQ to XML の開発者のコードは、通常、要素や属性よりも細かい細分性レベルで動作する必要があります。 しばしばノード レベルで、テキスト ノードを操作し、命令を処理し、コメントを処理する必要があります。 この記事では、ノード レベルでのプログラミングについて説明します。

例: null に設定された XDocument の子ノードの Parent プロパティ値

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 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)

この例を実行すると、次の出力が生成されます。

>><<

例: 空のテキスト ノードが 1 つある要素は、テキスト ノードのないものとは別にシリアル化される

要素に空の 1 つの子テキスト ノードのみが含まれている場合、その要素は長いタグ構文 <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='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