2011-04-28 28 views
29

¿Por qué este Xpath no funciona con XDocument.XPathSelectElement?XPathSelectElement siempre devuelve nulo

XPath:

//Plugin/UI[1]/PluginPageCategory[1]/Page[1]/Group[1]/CommandRef[2] 

XML

<Plugin xmlns="http://www.MyNamespace.ca/MyPath"> 
    <UI> 
    <PluginPageCategory> 
     <Page> 
     <Group> 
      <CommandRef> 
      <Images> 
      </Images> 
      </CommandRef> 
      <CommandRef> 
      <Images> 
      </Images> 
      </CommandRef> 
     </Group> 
     </Page> 
    </PluginPageCategory> 
    </UI> 
</Plugin> 

C# Código:

myXDocument.XPathSelectElement("//Plugin/UI[1]/PluginPageCategory[1]/Page[1]/Group[1]/CommandRef[2]", myXDocument.Root.CreateNavigator()); 
+4

No hay información de espacio de nombres en la consulta xpath, que puede ser la causa. Intenta, para reducir esto, eliminar el espacio de nombres en el XML y ver si eso te da un resultado. – Cumbayah

+2

Probablemente se deba al espacio de nombres; compruebe si eliminarlo del XML lo corrige, y si lo hace, debe configurar un NamespaceManager. –

+0

@Cumbayah: ¡pásame! –

Respuesta

24

Cuando se utilizan espacios de nombres, éstas deben utilizarse en la consulta XPath también. Su consulta XPath solo funcionaría contra elementos sin espacio de nombres (como se puede verificar eliminando el espacio de nombres de su XML).

He aquí un ejemplo que muestra cómo se crea y pasa un gestor de espacio de nombres:

var xml = ... XML from your post ...; 

var xmlReader = XmlReader.Create(new StringReader(xml)); // Or whatever your source is, of course. 
var myXDocument = XDocument.Load(xmlReader); 
var namespaceManager = new XmlNamespaceManager(xmlReader.NameTable); // We now have a namespace manager that knows of the namespaces used in your document. 
namespaceManager.AddNamespace("prefix", "http://www.MyNamespace.ca/MyPath"); // We add an explicit prefix mapping for our query. 

var result = myXDocument.XPathSelectElement(
    "//prefix:Plugin/prefix:UI[1]/prefix:PluginPageCategory[1]/prefix:Page[1]/prefix:Group[1]/prefix:CommandRef[2]", 
    namespaceManager 
); // We use that prefix against the elements in the query. 

Console.WriteLine(result); // <CommandRef ...> element is printed. 

Espero que esto ayude.

+42

Qué horrible código. Los espacios de nombres XML son cosas realmente feas. –

+0

¿Cuál es el punto de cargar el 'NameTable' del XmlReader si tiene que agregar explícitamente una combinación de prefix/ns? Supongo que está haciendo eso porque no conoce el prefijo que el documento está usando para el espacio de nombre deseado. Pero, ¿no significaría también que no es necesario inicializar 'XmlNamespaceManager' con' NameTable'? – crush

-2

Hay una manera de hacerlo sin ningún cambio en el XPath. La solución que he encontrado es eliminar el espacio de nombres al analizar XML en XDocument.

Aquí es un exemple:

var regex = @"(xmlns:?[^=]*=[""][^""]*[""])"; 
var myXDocument = XDocument.Parse(Regex.Replace("MyXmlContent", regex, "", RegexOptions.IgnoreCase | RegexOptions.Multiline)) 

Ahora que el espacio de nombres se ha ido, es más facil de manipular.

+6

_ "Ahora que el espacio de nombre se ha ido" _ es posible que no tenga un documento XML bien formado ... –

+13

Solución de fábrica de salchichas. –

+2

Bueno ... funciona perfectamente para mí. Y tenga en cuenta que puede ser útil cuando no puede cambiar el xpath. –

14

Esto probablemente debería ser un comentario en el post de @ Cumbayah, pero me parece que no puede dejar comentarios sobre cualquier cosa.

usted es probablemente mejor usar algo como esto en lugar de utilizar XmlReader para obtener la tabla de nombres.

var xml = ... XML from your post ...; 
var myXDocument = XDocument.Parse(xml); 
var namespaceManager = new XmlNamespaceManager(new NameTable()); 
namespaceManager.AddNamespace("prefix", "http://www.MyNamespace.ca/MyPath"); 

var result = ...; 
+1

Solo quiero señalar el problema de que el espacio de nombre predeterminado debe tener el prefijo. Es decir. namespaceManager.AddNamespace ("", "http://www.MyNamespace.ca/MyPath") hará que la propiedad DefaultNamespace sea correcta como se esperaba, pero XPathSelectElement no lo utilizará. ¡¡¡Engañoso!!! –

2

La forma más fácil en su caso es el uso de XPath ejes y prueba de nodo del nombre de nodo y la posición para seleccionar el elemento. Su selección XPath:

myXDocument.XPathSelectElement("//Plugin/UI[1]/PluginPageCategory[1]/Page[1]/Group[1]/CommandRef[2]", myXDocument.Root.CreateNavigator()); 

Se puede traducir fácilmente a:

myXDocument.XPathSelectElement("/child::node()[local-name()='Plugin']/child::node()[local-name()='UI'][position()=1]/child::node()[local-name()='PluginPageCategory'][position()=1]/child::node()[local-name()='Page'][position()=1]/child::node()[local-name()='Group'][position()=1]/child::node()[local-name()='CommandRef'][position()=2]"); 

No hay necesidad de crear y transmitir XmlNamespaceManager como parámetro.

+1

Impresionante, esto funciona genial. Los espacios de nombres deben ser simples pero son muy complicados, por lo que esta solución es muy útil. –

Cuestiones relacionadas