使用节点编程(LINQ to XML)

需要编写 XML 编辑器、转换系统或报表编写器等程序的 LINQ to XML 开发人员通常需要比元素和属性更精细级别的代码。 它们通常需要在节点级别工作,操作文本节点、处理指令和注释。 本文提供有关节点级别的编程的信息。

示例: Parent 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 编程模型中,相邻的文本节点始终合并。 这有时称为文本节点的规范化。 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 中,命名空间作为对象存储在 XAttribute XML 树中。 如果循环访问包含命名空间声明的元素的属性,命名空间声明是返回的集合中的项之一。 该 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 对象模型没有将空白符添加为文档子节点。因此,如果使用 XDocument 轴循环访问 Nodes 的子级,将会返回空白符文本节点。 但是,当您使用 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