Programmation avec des nœuds (LINQ to XML)

Les développeurs LINQ to XML qui ont besoin d’écrire des programmes, par exemple un éditeur XML, un système de transformation ou un générateur de rapports, ont souvent besoin d’un code qui fonctionne à un niveau de granularité plus fin que les éléments et les attributs. Ils doivent souvent travailler au niveau des nœuds, manipuler des nœuds de texte et traiter des instructions et des commentaires. Cet article fournit des informations sur la programmation au niveau du nœud.

Exemple : Les valeurs de la propriété Parent des nœuds enfants de XDocument sont définies sur null

La propriété Parent contient le XElement parent, et non le nœud parent. Les nœuds enfants de XDocument n'ont aucun XElement parent. Leur parent étant le document, la propriété Parent de ces nœuds est définie sur null.

Cela est illustré par l'exemple suivant :

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)

Cet exemple produit la sortie suivante :

True
True

Exemple : L’ajout de texte peut ou non créer un nœud de texte

Dans un certain nombre de modèles de programmation XML, les nœuds de texte adjacents sont toujours fusionnés. On appelle parfois cela la normalisation des nœuds de texte. LINQ to XML ne normalise pas les nœuds de texte. L'ajout de deux nœuds de texte au même élément entraîne l'existence de nœuds de texte adjacents. Cependant, si vous ajoutez du contenu spécifié en tant que chaîne et non en tant que nœud XText, LINQ to XML peut fusionner la chaîne avec un nœud de texte adjacent. l’exemple ci-dessous illustre ce cas de figure.

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

Cet exemple produit la sortie suivante :

1
1
2

Exemple : Le fait d’attribuer comme valeur de nœud de texte la chaîne vide n’a pas pour effet de supprimer le nœud

Dans certains modèles de programmation XML, les nœuds de texte sont assurés de ne pas contenir la chaîne vide. Le raisonnement est qu'un tel nœud de texte n'a aucun impact sur la sérialisation du code XML. Toutefois, pour la même raison que les nœuds de texte adjacents sont possibles, si vous supprimez le texte d’un nœud de texte en lui attribuant comme valeur la chaîne vide, le nœud de texte lui-même ne sera pas supprimé.

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)

Cet exemple produit la sortie suivante :

>><<

Exemple : Un élément avec un nœud de texte vide n’est pas sérialisé de la même façon qu’un élément sans nœud de texte

Si un élément contient uniquement un nœud de texte enfant vide, il est sérialisé avec la syntaxe de balise longue : <Child></Child>. Si un élément ne contient aucun nœud enfant, il est sérialisé avec la syntaxe de balise courte : <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)

Cet exemple produit la sortie suivante :

<Child1></Child1>
<Child2 />

Exemple : Les espaces de noms sont des attributs dans l’arborescence LINQ to XML

Bien que les déclarations d’espaces de noms aient une syntaxe identique aux attributs, dans certaines interfaces de programmation, telles que XSLT et XPath, les déclarations d’espaces de noms ne sont pas considérées comme des attributs. Cependant, dans LINQ to XML, les espaces de noms sont stockés en tant qu’objets XAttribute dans l’arborescence XML. Si vous itérez au sein des attributs d’un élément qui contient une déclaration d’espace de noms, celle-ci compte parmi les éléments de la collection retournée. La propriété IsNamespaceDeclaration indique si un attribut est une déclaration d'espace de noms.

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

Cet exemple produit la sortie suivante :

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

Exemple : Les méthodes d’axe XPath ne retournent pas les nœuds de texte enfants de XDocument

LINQ to XML tient compte des nœuds de texte enfants d’un objet XDocument, à condition qu’ils contiennent uniquement des espaces blancs. Or, le modèle d’objet XPath n’inclut pas les espaces blancs en tant que nœuds enfants d’un document ; par conséquent, lorsque vous itérez au sein des enfants d’un objet XDocument en utilisant l’axe Nodes, les nœuds de texte avec espaces blancs sont retournés. En revanche, quand vous itérez au sein des enfants d’un objet XDocument en utilisant les méthodes d’axe XPath, les nœuds de texte avec espaces blancs ne sont pas retournés.

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

Cet exemple produit la sortie suivante :

3
0

Le nœud de déclaration XML d’un objet XDocument est une propriété, pas un nœud enfant

Quand vous itérez au sein des enfants nœuds d’un objet XDocument, vous ne voyez pas l’objet de déclaration XML. Il s’agit d’une propriété du document, et non d’un de ses nœuds enfants.

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

Cet exemple produit la sortie suivante :

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