jak używać XPath z XDocument?

109

Jest podobne pytanie, ale wygląda na to, że rozwiązanie w moim przypadku nie zadziałało: Dziwność z XDocument, XPath i przestrzeniami nazw

Oto XML, z którym pracuję:

<?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>

A poniżej jest kod, który moim zdaniem powinien działać, ale nie ...

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

Czy ktoś ma jakieś pomysły? Dzięki.

jojo
źródło
1
Zobacz inną odpowiedź poniżej, nie działa, ponieważ implementacja XPath 1.0 nie radzi sobie z pustym prefiksem
Paul Hatcher,
1
Jak wspomniano tutaj, nie używaj pustego prefiksu podczas dodawania przestrzeni nazw do [XmlNamespaceManager]. Po prostu dodaję ten komentarz na wypadek, gdyby ktoś chciał zobaczyć mały przykład kodu z dokumentem, który ma kilka atrybutów [xmlns], z sufiksem i bez niego. Zobacz tutaj: stackoverflow.com/a/38272604/5838538
Jelgab

Odpowiedzi:

158

Jeśli masz XDocument, łatwiej jest użyć LINQ-to-XML:

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

Jeśli masz pewność, że XPath jest jedynym rozwiązaniem, którego potrzebujesz:

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
źródło
13
Powiedziałbym, że w większości przypadków trudno powiedzieć, że linq jest łatwiejsze niż xpath. Na przykład w tym przypadku odpowiednik LINQ nie jest tak naprawdę równoważny, ponieważ mógłby również uzyskać węzły „Nazwa” w innych węzłach (których teraz nie ma, ale można je dodać przy późniejszych zmianach formatu pliku). Jednak Twoje rozwiązanie jest z pewnością właściwe.
Marco Mp
12
UWAGA: using System.Xml.XPath; jest bardzo ważne, ponieważ XPathSelectElement jest metodą rozszerzającą. Nie rób tak jak ja i zignoruj ​​tę część;)
Mark van Straten
7
XPath jest nadal pomocny, ponieważ umożliwia kontekstualizację relacji rodzic-dziecko. Np. Jeśli chciałeś dostać się do / Banana / Banana / Banana zamiast dostawać każdego banana
Sebastian Patten
2
„pusty” jest tutaj nieco mylący i zagmatwany. Możesz użyć wszystkiego oprócz XPath, String.Empty (jak odkrył pytający). „demo” byłoby bardziej odpowiednie do tego przykładu.
Tom Blodget
7

XPath 1.0, który implementuje MS, nie ma pojęcia o domyślnej przestrzeni nazw. Więc spróbuj tego:

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
źródło
8
Twoja odpowiedź sugeruje, że XPath 2.0, w przeciwieństwie do XPath 1.0 "*, ma" pomysł "na domyślną przestrzeń nazw. Nie znam takiej nowej funkcji XPath (mówimy tutaj o XPath, a nie o XSLT lub XQuery). Czy mógłbyś zatem , proszę, wyraźnie wspomnij w swojej odpowiedzi, co sugerujesz?
Dimitre Novatchev
2
Myślę, że chodzi o to, że jeśli masz dokument, który definiuje przestrzeń nazw, twoja ścieżka xpath musi zawierać elementy kwalifikowane, tj. Nie możesz zrobić xnm.AddNamespace (string.Empty, " demo.com/2011/demo-schema" ); a następnie xdoc.XPathSelectElement ("/ Report / ReportInfo / Name", xnm) - wynik zawsze wychodzi na zero
Paul Hatcher
3

możesz skorzystać z przykładu firmy Microsoft - dla Ciebie bez przestrzeni nazw:

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

powinien to zrobić

Bernhard
źródło
nie działa dla mnie
user1623521