XPATHS и пространства имен по умолчанию

Какова история XPath и поддержки пространств имен? Предшествовал ли XPath как спецификация пространствам имен? Если у меня есть документ, в котором элементам присвоено пространство имен по умолчанию:

<foo xmlns="uri" />

Похоже, что некоторые библиотеки процессоров XPath не распознают //foo из-за пространства имен, тогда как другие распознают . Моя команда подумала о возможности добавить префикс пространства имен с использованием регулярных выражений в XPath (вы можете добавить префикс пространства имен через XmlNameTable), но это кажется хрупким, поскольку XPath - такой гибкий язык, когда дело доходит до тестов узлов.

Есть ли стандарт, который к этому применим?

Мой подход немного хакерский, но, похоже, он работает нормально; Я удаляю xmlns объявление поиском / заменой, а затем применяю XPath.

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

Это справедливый подход или кто-то решил это по-другому?

Ответов (5)

Решение

Я попробовал что-то похожее на то, что предлагал Palehorse, и не смог заставить его работать. Поскольку я получал данные из опубликованной службы, я не мог изменить xml. В итоге я использовал XmlDocument и XmlNamespaceManager вот так:

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

Если вы пытаетесь использовать xslt, вы можете добавить пространство имен в объявление таблицы стилей. Если вы это сделаете, вы должны убедиться, что префикс есть, иначе он не будет работать. Если исходный XML не имеет префикса, это все еще нормально, вы добавляете свой собственный префикс в таблицу стилей.

Таблица стилей

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

Или что-то подобное.

Используя libxml, кажется, что это работает:

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);
}

Вам нужно local-name ():

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

В кроватку с http://web.archive.org/web/20100810142303/http://jcooney.net:80/archive/2005/08/09/6517.aspx :

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

Это выражение будет соответствовать элементу «bar»:

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

Этот не будет:

 //bar

Проблема в том, что элемент без пространства имен объявляется находящимся в пространстве имен NULL - поэтому, если // foo сопоставляется с пространством имен, которое вы считаете «по умолчанию», не будет возможности ссылаться на элемент в пространстве имен null.

Помните также, что префикс для пространства имен - это только сокращенное соглашение, реальное имя элемента (полное имя или кратко QName) состоит из полного пространства имен и локального имени. Изменение префикса для пространства имен не меняет «идентичности» элемента - если он находится в том же пространстве имен и с тем же локальным именем, то это тот же тип элемента, даже если префикс отличается.

XPath 2.0 (или, скорее, XSLT 2.0) имеет концепцию «пространства имен xpath по умолчанию». Вы можете установить атрибут xpath-default-namespace в элементе xsl: stylesheet.