Compartir a través de


Programación con nodos (LINQ to XML)

Los desarrolladores de LINQ to XML que necesitan escribir programas como un editor XML, un sistema de transformación o un escritor de informes suelen necesitar código que funcione en un nivel más preciso de granularidad que los elementos y atributos. A menudo necesitan trabajar en el nivel de nodo, manipular nodos de texto, procesar instrucciones y procesar comentarios. En este artículo se proporciona información sobre la programación en el nivel de nodo.

Ejemplo: Los Parent valores de propiedad de los nodos secundarios de XDocument se establecen en null

La Parent propiedad contiene el elemento padre XElement, no el nodo padre. Los nodos hijo de XDocument no tienen nodo padre XElement. Su elemento primario es el documento, por lo que la Parent propiedad de esos nodos se establece en null.

En el ejemplo siguiente se muestra lo siguiente:

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)

En este ejemplo se genera la siguiente salida:

True
True

Ejemplo: Agregar texto puede o no crear un nuevo nodo de texto

En varios modelos de programación XML, los nodos de texto adyacentes siempre se combinan. A veces se denomina normalización de nodos de texto. LINQ to XML no normaliza los nodos de texto. Si agrega dos nodos de texto al mismo elemento, se producirán nodos de texto adyacentes. Sin embargo, si agrega contenido especificado como una cadena en lugar de como un XText nodo, LINQ to XML podría combinar la cadena con un nodo de texto adyacente. En el siguiente ejemplo se muestra cómo hacerlo.

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

En este ejemplo se genera la siguiente salida:

1
1
2

Ejemplo: Establecer un valor de nodo de texto en la cadena vacía no elimina el nodo

En algunos modelos de programación XML, se garantiza que los nodos de texto no contengan la cadena vacía. El razonamiento es que este nodo de texto no tiene ningún impacto en la serialización del XML. Sin embargo, por el mismo motivo que los nodos de texto adyacentes son posibles, si quita el texto de un nodo de texto estableciendo su valor en la cadena vacía, el propio nodo de texto no se eliminará.

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)

En este ejemplo se genera la siguiente salida:

>><<

Ejemplo: Un elemento con un nodo de texto vacío se serializa de forma diferente a uno sin nodo de texto

Si un elemento contiene solo un nodo de texto secundario que está vacío, se serializa con la sintaxis de etiqueta larga: <Child></Child>. Si un elemento no contiene ningún nodo secundario, se serializa con la sintaxis de etiqueta corta: <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)

En este ejemplo se genera la siguiente salida:

<Child1></Child1>
<Child2 />

Ejemplo: Los espacios de nombres son atributos en el árbol LINQ to XML

Aunque las declaraciones de espacio de nombres tienen una sintaxis idéntica a los atributos, en algunas interfaces de programación, como XSLT y XPath, las declaraciones de espacio de nombres no se consideran atributos. No obstante, en LINQ to XML, los espacios de nombres se almacenan como objetos XAttribute en el árbol XML. Si recorre en iteración los atributos o un elemento que contiene una declaración de espacio de nombres, la declaración de espacio de nombres es uno de los elementos de la recopilación devuelta. La IsNamespaceDeclaration propiedad indica si un atributo es una declaración de espacio de nombres.

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

En este ejemplo se genera la siguiente salida:

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

Ejemplo: los métodos del eje XPath no devuelven los nodos de texto secundarios de XDocument

LINQ to XML permite nodos de texto secundarios de un XDocument, siempre y cuando los nodos de texto solo contengan espacios en blanco. Sin embargo, el modelo de objetos XPath no incluye espacios en blanco como nodos secundarios de un documento, por lo que al iterar los elementos secundarios de un XDocument mediante el eje Nodes, se devolverán nodos de texto de espacio en blanco. Sin embargo, cuando recorra en iteración los elementos secundarios de XDocument usando los métodos del eje de XPath, no se devolverán los nodos de texto de espacio en blanco.

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

En este ejemplo se genera la siguiente salida:

3
0

El nodo de declaración XML de un XDocument es una propiedad, no un nodo secundario

Cuando iteres los nodos secundarios de un XDocument, no verás el objeto de declaración XML. Es una propiedad del documento, no un nodo secundario de él.

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

En este ejemplo se genera la siguiente salida:

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