comment utiliser XPath avec XDocument?

109

Il y a une question similaire, mais il semble que la solution n'a pas fonctionné dans mon cas: bizarrerie avec XDocument, XPath et les espaces de noms

Voici le XML avec lequel je travaille:

<?xml version="1.0" encoding="utf-8"?>
<Report Id="ID1" Type="Demo Report" Created="2011-01-01T01:01:01+11:00" Culture="en" xmlns="http://demo.com/2011/demo-schema">
    <ReportInfo>
        <Name>Demo Report</Name>
        <CreatedBy>Unit Test</CreatedBy>
    </ReportInfo>
</Report>

Et ci-dessous est le code que je pensais que cela devrait fonctionner mais cela n'a pas fonctionné ...

XDocument xdoc = XDocument.Load(@"C:\SampleXML.xml");
XmlNamespaceManager xnm = new XmlNamespaceManager(new NameTable()); 
xnm.AddNamespace(String.Empty, "http://demo.com/2011/demo-schema");
Console.WriteLine(xdoc.XPathSelectElement("/Report/ReportInfo/Name", xnm) == null);

Quelqu'un a-t-il une idée? Merci.

jojo
la source
1
Voir l'autre réponse ci-dessous, cela ne fonctionne pas car l'implémentation XPath 1.0 ne peut pas faire face à un préfixe vide
Paul Hatcher
1
Comme d'autres l'ont dit ici, n'utilisez pas de préfixe vide lors de l'ajout d'un espace de noms au [XmlNamespaceManager]. J'ajoute simplement ce commentaire au cas où quelqu'un voudrait voir un petit exemple de code avec un document qui a plusieurs attributs [xmlns], avec et sans suffixe. Voir ici: stackoverflow.com/a/38272604/5838538
Jelgab

Réponses:

158

Si vous avez XDocument, il est plus facile d'utiliser LINQ-to-XML:

var document = XDocument.Load(fileName);
var name = document.Descendants(XName.Get("Name", @"http://demo.com/2011/demo-schema")).First().Value;

Si vous êtes sûr que XPath est la seule solution dont vous avez besoin:

using System.Xml.XPath;

var document = XDocument.Load(fileName);
var namespaceManager = new XmlNamespaceManager(new NameTable());
namespaceManager.AddNamespace("empty", "http://demo.com/2011/demo-schema");
var name = document.XPathSelectElement("/empty:Report/empty:ReportInfo/empty:Name", namespaceManager).Value;
Alex Aza
la source
13
Je dirais qu'il est difficile de dire que linq est plus facile que xpath dans la plupart des cas. Par exemple, dans ce cas, l'équivalent LINQ n'est pas vraiment équivalent car il obtiendrait également des nœuds "Nom" sous d'autres nœuds (qui ne sont pas là maintenant mais pourraient être ajoutés par des modifications ultérieures du format du fichier). Cependant, votre solution est sûrement la bonne.
Marco Mp
12
REMARQUE: l'utilisation de System.Xml.XPath; est assez important car XPathSelectElement est une méthode d'extension. Ne faites pas ce que j'ai fait et ignorez cette partie;)
Mark van Straten
7
XPath est toujours utile dans la mesure où il vous permet de contextualiser vos relations parent-enfant. Par exemple, si vous vouliez vous rendre à / Banana / Banana / Banana au lieu d'obtenir chaque banane
Sebastian Patten
2
«vide» est un peu trompeur et déroutant ici. Vous pouvez utiliser n'importe quoi sauf, avec XPath, String.Empty (comme le demandeur l'a découvert). "demo" serait plus approprié à l'exemple.
Tom Blodget
7

XPath 1.0, qui est ce que MS implémente, n'a pas l'idée d'un espace de noms par défaut. Alors essayez ceci:

XDocument xdoc = XDocument.Load(@"C:\SampleXML.xml");
XmlNamespaceManager xnm = new XmlNamespaceManager(new NameTable()); 
xnm.AddNamespace("x", "http://demo.com/2011/demo-schema");
Console.WriteLine(xdoc.XPathSelectElement("/x:Report/x:ReportInfo/x:Name", xnm) == null);
Richard Schneider
la source
8
Votre réponse implique que XPath 2.0, contrairement à XPath 1.0 "* a" une idée "d'un espace de noms par défaut. Je ne suis pas au courant de cette nouvelle fonctionnalité XPath (nous parlons ici de XPath, pas de XSLT ou XQuery). Par conséquent, pourriez-vous , s'il vous plaît, mentionnez explicitement dans votre réponse ce que vous sous
entendez
2
Je pense que ce qu'il veut ici, c'est que si vous avez un document qui définit un espace de noms, votre xpath doit inclure des éléments qualifiés, c'est-à-dire que vous ne pouvez pas faire xnm.AddNamespace (string.Empty, " demo.com/2011/demo-schema" ); puis xdoc.XPathSelectElement ("/ Report / ReportInfo / Name", xnm) - le résultat sort toujours nul
Paul Hatcher
3

vous pouvez utiliser l'exemple de Microsoft - pour vous sans espace de noms:

using System.Xml.Linq;
using System.Xml.XPath;
var e = xdoc.XPathSelectElement("./Report/ReportInfo/Name");     

devrait le faire

Bernhard
la source
ne fonctionne pas pour moi
user1623521