¿Cómo utilizar XPath con XDocument?

109

Hay una pregunta similar, pero parece que la solución no funcionó en mi caso: rareza con XDocument, XPath y los espacios de nombres

Aquí está el XML con el que estoy trabajando:

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

Y a continuación está el código que pensé que debería estar funcionando pero no lo hizo ...

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

¿Alguien tiene alguna idea? Gracias.

jojo
fuente
1
Vea la otra respuesta a continuación, no funciona ya que la implementación de XPath 1.0 no puede hacer frente a un prefijo vacío
Paul Hatcher
1
Como otros dijeron aquí, no use un prefijo vacío al agregar un espacio de nombres al [XmlNamespaceManager]. Solo agrego este comentario en caso de que alguien quiera ver un pequeño ejemplo de código con un documento que tiene varios atributos [xmlns], con y sin sufijo. Vea aquí: stackoverflow.com/a/38272604/5838538
Jelgab

Respuestas:

158

Si tiene XDocument, es más fácil usar LINQ-to-XML:

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

Si está seguro de que XPath es la única solución que necesita:

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
fuente
13
Yo diría que es difícil decir que linq sea más fácil que xpath en la mayoría de los casos. Por ejemplo, en este caso, el equivalente de LINQ no es realmente equivalente, ya que también obtendría nodos de "Nombre" debajo de otros nodos (que no están ahora pero que podrían agregarse mediante cambios posteriores en el formato del archivo). Sin embargo, su solución es seguramente la correcta.
Marco Mp
12
NOTA: el uso de System.Xml.XPath; es bastante importante porque XPathSelectElement es un método de extensión. No hagas lo que hice e ignora esa parte;)
Mark van Straten
7
XPath sigue siendo útil porque le permite contextualizar sus relaciones entre padres e hijos. Por ejemplo, si quisieras ir a / Banana / Banana / Banana en lugar de obtener todas las bananas
Sebastian Patten
2
"vacío" es un poco engañoso y confuso aquí. Puede usar cualquier cosa excepto, con XPath, String.Empty (como descubrió el autor de la pregunta). "demo" sería más apropiado para el ejemplo.
Tom Blodget
7

XPath 1.0, que es lo que implementa MS, no tiene la idea de un espacio de nombres predeterminado. Así que prueba esto:

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
fuente
8
Su respuesta implica que XPath 2.0, a diferencia de XPath 1.0 "*, tiene" una idea "de un espacio de nombres predeterminado. No conozco esta nueva función XPath (estamos hablando de XPath aquí, no XSLT o XQuery). Por lo tanto, ¿podría , por favor, mencione explícitamente en su respuesta lo que está insinuando
Dimitre Novatchev
2
Creo que lo que quiere decir aquí es que si tiene un documento que define un espacio de nombres, su xpath debe incluir elementos calificados, es decir, no puede hacer xnm.AddNamespace (string.Empty, " demo.com/2011/demo-schema" ); y luego xdoc.XPathSelectElement ("/ Report / ReportInfo / Name", xnm) - el resultado siempre sale nulo
Paul Hatcher
3

puede usar el ejemplo de Microsoft , para usted sin espacio de nombres:

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

Deberías hacerlo

Bernhard
fuente
no funciona para mí
user1623521