Compartilhar via


Programação com nós (LINQ to XML)

Os desenvolvedores de LINQ to XML que precisam escrever programas como um editor de XML, um sistema de transformação ou um gravador de relatórios geralmente precisam de código que funcione em um nível de granularidade mais refinado do que elementos e atributos. Eles frequentemente precisam trabalhar no nível do nó, manipulando nós de texto, instruções de processamento e comentários de processamento. Este artigo fornece informações sobre a programação no nível do nó.

Exemplo: os valores de propriedade Parent dos nós filho do XDocument são definidos como null

A propriedade de Parent contém XElementpai, não o nó pai. Os nós filho de XDocument não têm nenhum XElementpai. Seu pai é o documento, portanto, a propriedade Parent para esses nós é definida como null.

O exemplo a seguir demonstra este:

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)

Esse exemplo gera a saída a seguir:

True
True

Exemplo: a adição de texto pode ou não criar um novo nó de texto

Em um número XML que programa modelos, nós adjacentes de texto sempre são mesclados. Isso é às vezes chamado normalização de nós de texto. O LINQ to XML não normaliza nós de texto. Se você adicionar dois nós de texto ao mesmo elemento, resultará a nós adjacentes de texto. Entretanto, se você adicionar o conteúdo especificado como uma cadeia de caracteres em vez de como um nó de XText, o LINQ to XML poderá mesclar a cadeia de caracteres com um nó de texto adjacente. O exemplo a seguir demonstra isso.

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

Esse exemplo gera a saída a seguir:

1
1
2

Exemplo: definir um valor de nó de texto para a cadeia de caracteres vazia não exclui o nó

Em algum XML que programa modelos, os nós de texto são garantidos para não conter a cadeia de caracteres vazia. O raciocínio é que um nó de texto não tem impacto na serialização XML. No entanto, pelo mesmo motivo que os nós de texto adjacentes são possíveis, se você remover o texto de um nó de texto ao definir seu valor como a cadeia de caracteres vazia, o próprio nó de texto não será excluído.

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)

Esse exemplo gera a saída a seguir:

>><<

Exemplo: um elemento com um nó de texto vazio é serializado de forma diferente de um sem nó de texto

Se um elemento contiver apenas um nó de texto filho vazio, ele será serializado com a sintaxe de marca longa: <Child></Child>. Se um elemento não contiver nenhum nó filho, ele será serializado com a sintaxe de marca curta: <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)

Esse exemplo gera a saída a seguir:

<Child1></Child1>
<Child2 />

Exemplo: namespaces são atributos na árvore LINQ to XML

Mesmo que as declarações de namespace tenham a sintaxe idêntica a atributos, em algumas interfaces de programação, como XSLT e o XPath, as declarações de namespace não são consideradas como atributos. No entanto, no LINQ to XML, os namespaces são armazenados como objetos XAttribute na árvore XML. Se você iterar através dos atributos de um elemento que contém uma declaração de namespace, a declaração de namespace será um dos itens na coleção retornada. A propriedade de IsNamespaceDeclaration indica se um atributo é uma declaração de namespace.

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

Esse exemplo gera a saída a seguir:

xmlns="http://www.adventure-works.com"  IsNamespaceDeclaration:True
xmlns:fc="www.fourthcoffee.com"  IsNamespaceDeclaration:True
AnAttribute="abc"  IsNamespaceDeclaration:False

Exemplo: os métodos do eixo XPath não retornam os nós de texto filho do XDocument

O LINQ to XML permite nós filhos de texto de um XDocument, desde que os nós de texto contenham somente espaço em branco. No entanto, o modelo de objeto XPath não inclui espaço em branco como nós filho de um documento, portanto, quando você itera através dos filhos de um XDocument usando o eixo Nodes, os nós de texto de espaço em branco serão retornados. No entanto, quando você itera através dos filhos de um XDocument usando os métodos do eixo XPath, os nós de texto de espaço em branco não serão retornados.

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

Esse exemplo gera a saída a seguir:

3
0

O nó de declaração XML de um XDocument é uma propriedade, não um nó filho

Quando você itera através de nós filhos de um XDocument, você não verá o objeto da declaração XML. É uma propriedade do documento, não um nó filho dele.

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

Esse exemplo gera a saída a seguir:

<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<Root />
1