Программирование с узлами (LINQ to XML)

Разработчикам LINQ to XML, которые должны писать такие программы, как редактор 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 оси будут возвращены текстовые узлы пробела. Однако при выполнении итерации дочерних XDocument элементов с помощью методов оси XPath текстовые узлы пробелов не будут возвращены.

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

Узел объявления XML xDocument является свойством, а не дочерним узлом

При итерации по дочерним узлам объекта 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