XPATHS и пространства имен по умолчанию
Какова история XPath и поддержки пространств имен? XPath как спецификация предшествовал пространствам имен? Если у меня есть документ, где элементам дано пространство имен по умолчанию:
<foo xmlns="uri" />
Похоже, что некоторые библиотеки процессора XPath не распознают //foo
из-за пространства имен, тогда как другие будут. Моя команда подумала о том, чтобы добавить префикс пространства имен с помощью регулярных выражений в XPath (вы можете добавить префикс пространства имен через XmlNameTable), но это кажется хрупким, поскольку XPath является таким гибким языком, когда дело доходит до тестов узлов.
Есть ли стандарт, который применяется к этому?
Мой подход немного хакерский, но, похоже, работает нормально; Я удаляю xmlns
объявление с поиском / заменой, а затем применить XPath.
string readyForXpath = Regex.Replace(xmldocument, "xmlns=\".+\"", String.Empty );
Это честный подход или кто-то решил это по-другому?
5 ответов
Я попробовал что-то похожее на то, что предложил Палехорс, и не смог заставить его работать. Так как я получал данные из опубликованного сервиса, я не мог изменить 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
Вам нужно 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 сопоставляется с пространством имен, которое вы считаете "по умолчанию", не будет никакого способа сослаться на элемент в пустом пространстве имен.
Также помните, что префикс для пространства имен - это только условное обозначение, реальное имя элемента (Qualified Name или QName для краткости) состоит из полного пространства имен и локального имени. Изменение префикса для пространства имен не меняет "идентичность" элемента - если он находится в том же пространстве имен и с тем же локальным именем, то это элемент того же типа, даже если префикс отличается.
XPath 2.0 (точнее, XSLT 2.0) имеет концепцию "пространства имен xpath по умолчанию". Вы можете установить атрибут xpath-default-namespace в элементе xsl:stylesheet.
С использованием 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);
}
Если вы пытаетесь использовать 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>
Или что-то типа того.