Programmazione con nodi (LINQ to XML)

Gli sviluppatori di LINQ to XML che hanno la necessità di scrivere programmi come un editor XML, un sistema di trasformazioni o un writer di rapporti, spesso devono scrivere codice che funziona a un livello di granularità più fine rispetto a elementi e attributi. Spesso hanno bisogno d lavorare a livello di nodo per manipolare i nodi di testo ed elaborare istruzioni e commenti. Questo articolo fornisce informazioni sulla programmazione a livello di nodo.

Esempio: i valori della proprietà Parent dei nodi figlio di XDocument sono impostati su null

La proprietà Parent contiene l'elemento padre XElement, non il nodo padre. I nodi figlio di XDocument non hanno un elemento XElement padre. L'elemento padre è il documento, pertanto la proprietà Parent per tali nodi è impostata su null.

Questo concetto è illustrato nell'esempio seguente:

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)

Nell'esempio viene prodotto l'output seguente:

True
True

Esempio: l'aggiunta di testo può o meno creare un nuovo nodo di testo

In diversi modelli di programmazione XML i nodi di testo adiacenti vengono sempre uniti. Questa operazione è denominata normalizzazione dei nodi di testo. LINQ to XML non normalizza i nodi di testo. Se si aggiungono due nodi di testo allo stesso elemento, si otterranno nodi di testo adiacenti. Se tuttavia si aggiunge il contenuto specificato come stringa anziché come nodo XText, è possibile che in LINQ to XML la stringa venga unita a un nodo di testo adiacente. L'esempio seguente illustra questa operazione.

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

Nell'esempio viene prodotto l'output seguente:

1
1
2

Esempio: l'impostazione di un valore del nodo di testo sulla stringa vuota non comporta l'eliminazione del nodo

In alcuni modelli di programmazione di XML è garantito che i nodi di testo non contengono la stringa vuota. Il concetto di base è che un nodo di testo di questo tipo non ha impatto sulla serializzazione dell'XML. Tuttavia, per lo stesso motivo per cui i nodi di testo adiacenti sono possibili, la rimozione di testo da un nodo di testo impostandone il valore sulla stringa vuota non comporta l'eliminazione del nodo di testo stesso.

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)

Nell'esempio viene prodotto l'output seguente:

>><<

Esempio: un elemento con un nodo di testo vuoto viene serializzato in modo diverso da uno senza nodo di testo

Se un elemento contiene solo un nodo di testo figlio vuoto, verrà serializzato con la sintassi lunga dei tag: <Child></Child>. Se un elemento non contiene nodi figlio, verrà serializzato con la sintassi breve dei tag: <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)

Nell'esempio viene prodotto l'output seguente:

<Child1></Child1>
<Child2 />

Esempio: gli spazi dei nomi sono attributi nell'albero LINQ to XML

Anche se la sintassi delle dichiarazioni di spazi dei nomi è identica a quella degli attributi, in alcune interfacce di programmazione, ad esempio XSLT e XPath, le dichiarazioni di spazi dei nomi non sono considerate attributi. Tuttavia, in LINQ to XML gli spazi dei nomi vengono archiviati come oggetti XAttribute nell'albero XML. Se si esegue l'iterazione degli attributi di un elemento che contiene una dichiarazione di spazio dei nomi, tale dichiarazione è uno degli elementi presenti nella raccolta restituita. La proprietà IsNamespaceDeclaration indica se un attributo è una dichiarazione di spazio dei nomi.

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

Nell'esempio viene prodotto l'output seguente:

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

Esempio: i metodi dell'asse XPath non restituiscono i nodi di testo figlio di XDocument

LINQ to XML consente di usare nodi di testo figlio di un oggetto XDocument, purché contengano solo spazi vuoti. Tuttavia, il modello a oggetti di XPath non include gli spazi vuoti come nodi figlio di un documento, quindi quando si esegue l'iterazione degli elementi figlio di un oggetto XDocument usando l'asse Nodes, verranno restituiti nodi di testo con spazi vuoti. Tuttavia, quando si esegue l'iterazione degli elementi figlio di un oggetto XDocument usando i metodi dell'asse di XPath, i nodi di testo con spazi vuoti non verranno restituiti.

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

Nell'esempio viene prodotto l'output seguente:

3
0

Il nodo di dichiarazione XML di un XDocument è una proprietà, non un nodo figlio

Quando si esegue l'interazione dei nodi figlio di un oggetto XDocument, l'oggetto dichiarazione XML non verrà visualizzato. Si tratta di una proprietà, non di un nodo figlio del documento.

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

Nell'esempio viene prodotto l'output seguente:

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