XPATHS и пространства имен по умолчанию
-
08-06-2019 - |
Вопрос
Какова история XPath и поддержки пространств имен?Предшествовал ли XPath в качестве спецификации пространствам имен?Если у меня есть документ, в котором элементам присвоено пространство имен по умолчанию:
<foo xmlns="uri" />
Похоже, что некоторые библиотеки процессора XPath не распознают //foo
из-за пространства имен, в то время как другие будут.Вариант, о котором подумала моя команда, - добавить префикс пространства имен с помощью регулярных выражений к XPath (вы можете добавить префикс пространства имен через XmlNameTable), но это кажется хрупким, поскольку XPath - такой гибкий язык, когда дело доходит до тестов узлов.
Есть ли стандарт, который применим к этому?
Мой подход немного хакерский, но, кажется, он работает нормально;Я удаляю xmlns
объявление с помощью поиска / замены, а затем применение XPath.
string readyForXpath = Regex.Replace(xmldocument, "xmlns=\".+\"", String.Empty );
Это справедливый подход или кто-нибудь решил это по-другому?
Решение
Я попробовал что-то похожее на то, что предложил 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
Другие советы
Вам нужно локальное имя():
http://www.w3.org/TR/xpath#function-local-name
В шпаргалку из http://jcooney.net/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.
Если вы пытаетесь использовать 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);
}