Domanda

Qual è la storia dietro XPath e il supporto per gli spazi dei nomi?XPath come specifica ha preceduto gli spazi dei nomi?Se ho un documento in cui agli elementi è stato assegnato uno spazio dei nomi predefinito:

<foo xmlns="uri" />

Sembra che alcune delle librerie del processore XPath non le riconoscano //foo a causa dello spazio dei nomi mentre altri lo faranno.L'opzione a cui ha pensato il mio team è quella di aggiungere un prefisso dello spazio dei nomi utilizzando espressioni regolari a XPath (puoi aggiungere un prefisso dello spazio dei nomi tramite XmlNameTable) ma questo sembra fragile poiché XPath è un linguaggio così flessibile quando si tratta di test dei nodi.

Esiste una norma che si applica a questo?

Il mio approccio è un po' hacker ma sembra funzionare bene;rimuovo il xmlns dichiarazione con una ricerca/sostituzione e quindi applicare XPath.

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

È un approccio giusto o qualcuno ha risolto il problema in modo diverso?

È stato utile?

Soluzione

Ho provato qualcosa di simile a quello proposto da Palehorse e non sono riuscito a farlo funzionare.Dato che ricevevo dati da un servizio pubblicato, non potevo modificare il file xml.Ho finito per usare XmlDocument e XmlNamespaceManager in questo modo:

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

Altri suggerimenti

Hai bisogno di nome-locale():

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

Da presepe http://jcooney.net/archive/2005/08/09/6517.aspx:

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

Questa espressione corrisponderà all'elemento "bar":

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

Questo non:

 //bar

Il problema è che un elemento senza uno spazio dei nomi viene dichiarato come nello spazio dei nomi NULL, pertanto se //foo corrispondesse allo spazio dei nomi che consideri "predefinito" non ci sarebbe modo di fare riferimento a un elemento nello spazio dei nomi null.

Ricordare inoltre che il prefisso per uno spazio dei nomi è solo una convenzione abbreviata, il nome dell'elemento reale (nome qualificato o QName in breve) è costituito dallo spazio dei nomi completo e dal nome locale.La modifica del prefisso per uno spazio dei nomi non cambia l'"identità" di un elemento: se si trova nello stesso spazio dei nomi e nello stesso nome locale, allora è lo stesso tipo di elemento, anche se il prefisso è diverso.

XPath 2.0 (o meglio XSLT 2.0) ha il concetto di "spazio dei nomi xpath predefinito".È possibile impostare l'attributo xpath-default-namespace sull'elemento xsl:stylesheet.

Se stai provando a utilizzare xslt puoi aggiungere lo spazio dei nomi alla dichiarazione del foglio di stile.Se lo fai, devi assicurarti che ci sia un prefisso altrimenti non funzionerà.Se l'XML di origine non ha un prefisso, va comunque bene, aggiungi il tuo prefisso nel foglio di stile.

Foglio di stile

<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 qualcosa di simile.

Usando libxml sembra che funzioni:

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);
}
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top