Программирование с использованием узлов
Обновлен: November 2007
Разработчикам LINQ to XML, которым требуется написать такие программы, как 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 соседние текстовые узлы всегда объединяются. Иногда такую операцию называют нормализацией текстовых узлов. 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>. Если элемент не содержит ни одного дочернего узла, он сериализуется c синтаксисом с короткими тегами: <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 пространства имен хранятся в XML-дереве как объекты XAttribute. Если просмотреть атрибуты элемента, который содержит декларацию пространства имен, то можно увидеть, что декларация пространства имен является одним из элементов возвращенной коллекции.
Свойство 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 не включает пробелы как дочерние узлы документа, поэтому при прохождении по дочерним узлам объекта 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
Объекты 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