Requête XPath et namespace XML par défaut
Les requêtes XPath permettent de faire du requetage sur un fichier XML afin de trouver facilement et rapidement un ou plusieurs noeuds XML.
En .net, on peut faire une requête XPath à partir d'un objet XmlNode via les méthodes SelectSingleNode ainsi que SelecNodes.
Par exemple si on a ce fichier XML :
<?xml version="1.0" encoding="utf-8" ?>
<Pouets>
<Pouet>Pouet N°1</Pouet>
</Pouets>
On peut obtenri le premier noeud Pouet ainsi :
XmlDocument doc = new XmlDocument();
doc.Load("pouet.xml");
Console.WriteLine(doc.SelectSingleNode("//Pouet") != null);
(Bien sur XPath est beaucoup plus puissant que ça, mais ce n'est pas l'objet du post.)
Malheureusement ça se gate si on travail avec des fichiers XML complexes : avec plusieurs namespaces XML. Par exemple si on a ce fichier :
<?xml version="1.0" encoding="utf-8" ?>
<Pouets xmlns="pouetNamespace">
<Pouet>Pouet N°1</Pouet>
</Pouets>
Le code plus haut ne fonctionne plus ! Après un tour dans la documentation, on peut utiliser un XmlNamespaceManager afin de résoudre les namespaces XML.
On pourrait alors écrire :
XmlDocument doc = new XmlDocument();
doc.Load("pouet.xml");
XmlNamespaceManager xmlnsmgr = new XmlNamespaceManager(doc.NameTable);
xmlnsmgr.AddNamespace(String.Empty, "pouetNamespace");
Console.WriteLine(doc.SelectSingleNode("//Pouet", xmlnsmgr) != null); // => doesn't work !
Mais cela ne fonctionne toujours pas. En lisant d'avantages la doc, il y a une partie sur les requêtes XPath et namespaces par défaut.
If the XPath expression does not include a prefix, it is assumed that the namespace URI is the empty namespace. If your XML includes a default namespace, you must still add a prefix and namespace URI to the XmlNamespaceManager; otherwise, you will not get a node selected. For more information, see Select Nodes Using XPath Navigation.
Pour requêter un fichier XML ayant un namespace par défaut, on est donc obligé de rajouter un namespace avec un prefixe, ce qui donne :
XmlDocument doc = new XmlDocument();
doc.Load("pouet.xml");
XmlNamespaceManager xmlnsmgr = new XmlNamespaceManager(doc.NameTable);
xmlnsmgr.AddNamespace("dummy", "pouetNamespace");
Console.WriteLine(doc.SelectSingleNode("//dummy:Pouet", xmlnsmgr) != null);
Je trouve ce comportement étonnant et absolument pas intuitif. De plus, l'impossibilité de récupérer un XmlNamespaceManager à partir d'un XmlDocument rend l'utilisation de XPath via .net sur des fichiers XML complexe très pénible. Je ne parle même pas de l'utilisation de XPath via XLinq (Linq to XML), qui est complètement impossible sauf en passant par un XmlReader ou le contenu littéral du XML.