2010-11-24 8 views
10

tengo algo de XML con espacio de nombres predeterminadoXML-SelectNodes con default-espacio de nombres a través de XmlNamespaceManager no funciona como se esperaba

<a xmlns='urn:test.Schema'><b/><b/></a> 

y quiero contar el número de <b/>

? ¿Cómo tengo que definir

XmlNamespaceManager nsmgr = ???? 
Assert.AreEqual(2, doc.SelectNodes("//b", nsmgr).Count); 

de manera que la aserción es verdadera?

que he probado hasta ahora (usando nunit):

[Test] 
[Ignore("Why does this not work?")] 
public void __DoesNotWork_TestSelectWithDefaultNamespace() 
{ 
    // xml to parse with defaultnamespace 
    string xml = @"<a xmlns='urn:test.Schema'><b/><b/></a>"; 

    XmlDocument doc = new XmlDocument(); 
    doc.LoadXml(xml); 

    // fails because xpath does not have the namespace 
    //!!!! 
    Assert.AreEqual(2, doc.SelectNodes("//b").Count); 

    // using XPath defaultnamespace 
    XmlNamespaceManager nsmgr = new XmlNamespaceManager(doc.NameTable); 
    nsmgr.AddNamespace("", "urn:test.Schema"); 

    // This will fail with dotnet 3.5sp1. Why? 
    //!!!! 
    Assert.AreEqual(2, doc.SelectNodes("//b", nsmgr).Count); 
} 

[Test] 
public void TestSelectWithoutNamespaces_Ok() 
{ 
    // xml to parse without namespace 
    string xml = @"<a><b/><b/></a>"; 

    XmlDocument doc = new XmlDocument(); 
    doc.LoadXml(xml); 

    // works ok 
    Assert.AreEqual(2, doc.SelectNodes("//b").Count); 

    // works ok 
    XmlNamespaceManager nsmgr = new XmlNamespaceManager(doc.NameTable); 
    Assert.AreEqual(2, doc.SelectNodes("//b", nsmgr).Count); 
} 

[Test] 
public void TestSelectWithNamespacesPrefixed_Ok() 
{ 
    // xml to parse with defaultnamespace 
    string xml = @"<a xmlns='urn:test.Schema'><b/><b/></a>"; 

    XmlDocument doc = new XmlDocument(); 
    doc.LoadXml(xml); 

    // using XPath namespace via alias "t". works ok but xpath is to complicated 
    XmlNamespaceManager nsmgr = new XmlNamespaceManager(doc.NameTable); 
    nsmgr.AddNamespace("t", doc.DocumentElement.NamespaceURI); 
    Assert.AreEqual(2, doc.SelectNodes("//t:b", nsmgr).Count); 
} 

Respuesta

27
// This will fail with dotnet 3.5sp1. Why? 
//!!!! 
Assert.AreEqual(2, doc.SelectNodes("//b", nsmgr).Count); 

Este es un FAQ. En XPath se supone que cualquier nombre no prefijado está en "sin espacio de nombres". Para seleccionar elementos que pertenecen a un espacio de nombres, en cualquier expresión XPath, sus nombres deben estar prefijados con un prefijo asociado a este espacio de nombres. El método AddNamespace() sirve exactamente para este propósito. Crea un enlace entre un espacio de nombre específico y un prefijo específico. Luego, si este prefijo se usa en una expresión XPath, se puede seleccionar el elemento con el prefijo.

Está escrito en el XPath W3C spec:. "Un QName en la prueba de nodo se expandió a un nombre expandido utilizando las declaraciones de espacio de nombres del contexto de la expresión Se trata de la misma manera que la expansión se realiza para los nombres de tipo de elemento en inicio y etiquetas finales excepto que no se usa el espacio de nombres predeterminado declarado con xmlns: si QName no tiene un prefijo, entonces el URI del espacio de nombres es nulo ".

Ver esto en: w3.org/TR/xpath/#node-tests.

Por lo tanto, cualquier nombre sin prefijo se considera que está en "ningún espacio de nombres". En el documento XML provisto no hay elementos b en "sin espacio de nombres" y esta es la razón por la cual la expresión XPath //b no selecciona ningún nodo.

Uso:

XmlNamespaceManager nsmanager = new XmlNamespaceManager(doc.NameTable); 
nsmanager.AddNamespace("x", "urn:test.Schema"); 

y más tarde:

Assert.AreEqual(2, doc.SelectNodes("//x:b", nsmanager).Count); 

Recuerde: Todo el propósito de registrar el espacio de nombres es ser capaz de utilizar el prefijo (en este caso x) en cualquier expresión XPath.

+0

gracias por su respuesta. usted describió lo mismo que lo que hace el tercer TestSelectWithNamespacesPrefixed_Ok() más inerte. Todavía espero que hay una workaroud sin la necesidad de modificar la expresión XPath- – k3b

+4

@ k3b: Usted escribió * Todavía espero que hay una workaroud sin la necesidad de modificar la expresión XPath *. No. Esto es preguntas frecuentes: una prueba de QName sin prefijo selecciona elementos en el URI nulo (o vacío) del espacio de nombres, no en el espacio de nombres predeterminado. –

+0

@ k3B: No, no hay tal solución: está escrito en la especificación XPath W3C: "Un QName en la prueba de nodo se expande en un nombre expandido usando las declaraciones de espacio de nombres del contexto de la expresión.Esta es la misma forma de expansión para los nombres de tipos de elementos en las etiquetas de inicio y final, excepto que no se usa el espacio de nombres predeterminado declarado con xmlns: si QName no tiene un prefijo, entonces el URI del espacio de nombres es nulo "ver esto en: http://www.w3.org/TR/xpath/#node-tests. Por lo tanto, cualquier nombre no prefijado se considera que está en "sin espacio de nombres". No hay elementos 'b' en" sin espacio de nombres "y obtienes 0 nodos –

Cuestiones relacionadas