Pergunta

Qual é a história por trás do XPath e do suporte para namespaces?O XPath como especificação precedeu os namespaces?Se eu tiver um documento onde os elementos receberam um namespace padrão:

<foo xmlns="uri" />

Parece que algumas das bibliotecas do processador XPath não reconhecem //foo por causa do namespace, enquanto outros o farão.A opção que minha equipe pensou é adicionar um prefixo de namespace usando expressões regulares ao XPath (você pode adicionar um prefixo de namespace via XmlNameTable), mas isso parece frágil, já que XPath é uma linguagem muito flexível quando se trata de testes de nó.

Existe um padrão que se aplica a isso?

Minha abordagem é um pouco hackeada, mas parece funcionar bem;Eu removo o xmlns declaração com uma pesquisa/substituição e, em seguida, aplique XPath.

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

Essa é uma abordagem justa ou alguém resolveu isso de forma diferente?

Foi útil?

Solução

Tentei algo semelhante ao que o palehorse propôs e não consegui fazer funcionar.Como estava obtendo dados de um serviço publicado, não consegui alterar o xml.Acabei usando XmlDocument e XmlNamespaceManager assim:

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

Outras dicas

Você precisa de nome local():

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

Para berço de http://jcooney.net/archive/2005/08/09/6517.aspx:

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

Esta expressão corresponderá ao elemento “bar”:

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

Este não vai:

 //bar

O problema é que um elemento sem um namespace é declarado como estando no namespace NULL - portanto, se //foo correspondesse ao namespace que você considera o 'padrão', não haveria como se referir a um elemento no namespace nulo.

Lembre-se também de que o prefixo de um namespace é apenas uma convenção abreviada, o nome real do elemento (Nome qualificado ou QName, abreviadamente) consiste no namespace completo e no nome local.Alterar o prefixo de um namespace não altera a 'identidade' de um elemento - se estiver no mesmo namespace e no mesmo nome local, então é o mesmo tipo de elemento, mesmo que o prefixo seja diferente.

XPath 2.0 (ou melhor, XSLT 2.0) tem o conceito de 'namespace xpath padrão'.Você pode definir o atributo xpath-default-namespace no elemento xsl:stylesheet.

Se você estiver tentando usar xslt, poderá adicionar o namespace à declaração da folha de estilo.Se você fizer isso, certifique-se de que existe um prefixo ou não funcionará.Se o XML de origem não tiver um prefixo, tudo bem, você adiciona seu próprio prefixo na folha de estilo.

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

Ou algo assim.

Usando libxml parece que 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 em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top