Pregunta

¿Cuál es la historia detrás de XPath y el soporte para espacios de nombres?¿Xpath como especificación precedió a los espacios de nombres?Si tengo un documento donde a los elementos se les ha asignado un espacio de nombres predeterminado:

<foo xmlns="uri" />

Parece que algunas de las bibliotecas del procesador XPath no reconocen //foo debido al espacio de nombres, mientras que otros lo harán.La opción que mi equipo ha pensado es agregar un prefijo de espacio de nombres usando expresiones regulares a XPath (puede agregar un prefijo de espacio de nombres a través de XmlNameTable), pero esto parece frágil ya que XPath es un lenguaje muy flexible cuando se trata de pruebas de nodos.

¿Existe alguna norma que se aplique a esto?

Mi enfoque es un poco complicado pero parece funcionar bien;elimino el xmlns declaración con una búsqueda/reemplazo y luego aplicar XPath.

string readyForXpath = Regex.Replace(xmldocument, "xmlns=\".+\"", String.Empty );

¿Es ese un enfoque justo o alguien ha resuelto esto de manera diferente?

¿Fue útil?

Solución

Intenté algo similar a lo que propuso palehorse y no pude hacerlo funcionar.Como estaba obteniendo datos de un servicio publicado, no pude cambiar el xml.Terminé usando XmlDocument y XmlNamespaceManager así:

XmlDocument doc = new XmlDocument();
doc.LoadXml(xmlWithBogusNamespace);            
XmlNamespaceManager nSpace = new XmlNamespaceManager(doc.NameTable);
nSpace.AddNamespace("myNs", "http://theirUri");

XmlNodeList nodes = doc.SelectNodes("//myNs:NodesIWant",nSpace);
//etc

Otros consejos

Necesitas nombre local():

http://www.w3.org/TR/xpath#function-local-name

a la cuna desde http://jcooney.net/archive/2005/08/09/6517.aspx:

<foo xmlns='urn:foo'>
  <bar>
    <asdf/>
  </bar>            
</foo>

Esta expresión coincidirá con el elemento "barra":

  //*[local-name()='bar'] 

Este no lo hará:

 //bar

El problema es que un elemento sin un espacio de nombres se declara en el espacio de nombres NULL; por lo tanto, si //foo coincide con el espacio de nombres que considera "predeterminado", no habría forma de hacer referencia a un elemento en el espacio de nombres nulo.

Recuerde también que el prefijo para un espacio de nombres es sólo una convención abreviada; el nombre real del elemento (nombre calificado o QName para abreviar) consta del espacio de nombres completo y el nombre local.Cambiar el prefijo de un espacio de nombres no cambia la "identidad" de un elemento; si está en el mismo espacio de nombres y en el mismo nombre local, entonces es el mismo tipo de elemento, incluso si el prefijo es diferente.

XPath 2.0 (o más bien XSLT 2.0) tiene el concepto de "espacio de nombres xpath predeterminado".Puede configurar el atributo xpath-default-namespace en el elemento xsl:stylesheet.

Si intenta utilizar xslt, puede agregar el espacio de nombres a la declaración de la hoja de estilo.Si haces eso, debes asegurarte de que haya un prefijo o no funcionará.Si el XML de origen no tiene un prefijo, aún está bien; agrega su propio prefijo en la hoja de estilo.

Hoja de estilo

<xsl:stylesheet
    xmlns:fb="uri"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">

    <xsl:template match="fb:foo/bar">
        <!--  do stuff here -->
    </xsl:template>
</xsl:stylsheet>

O algo así.

Usando libxml parece que esto funciona:

http://xmlsoft.org/examples/xpath1.c

 int 
register_namespaces(xmlXPathContextPtr xpathCtx, const xmlChar* nsList) {
    xmlChar* nsListDup;
    xmlChar* prefix;
    xmlChar* href;
    xmlChar* next;

    assert(xpathCtx);
    assert(nsList);

    nsListDup = xmlStrdup(nsList);
    if(nsListDup == NULL) {
    fprintf(stderr, "Error: unable to strdup namespaces list\n");
    return(-1); 
    }

    next = nsListDup; 
    while(next != NULL) {
    /* skip spaces */
    while((*next) == ' ') next++;
    if((*next) == '\0') break;

    /* find prefix */
    prefix = next;
    next = (xmlChar*)xmlStrchr(next, '=');
    if(next == NULL) {
        fprintf(stderr,"Error: invalid namespaces list format\n");
        xmlFree(nsListDup);
        return(-1); 
    }
    *(next++) = '\0';   

    /* find href */
    href = next;
    next = (xmlChar*)xmlStrchr(next, ' ');
    if(next != NULL) {
        *(next++) = '\0';   
    }

    /* do register namespace */
    if(xmlXPathRegisterNs(xpathCtx, prefix, href) != 0) {
        fprintf(stderr,"Error: unable to register NS with prefix=\"%s\" and href=\"%s\"\n", prefix, href);
        xmlFree(nsListDup);
        return(-1); 
    }
    }

    xmlFree(nsListDup);
    return(0);
}
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top