What is the story behind XPath and support for namespaces? Did XPath as a specification precede namespaces? If I have a document where elements have been given a default na
The issue is that an element without a namespace is declared to be in the NULL namespace - therefore if //foo matched against the namespace you consider to be the 'default' there would be no way to refer to an element in the null namespace.
Remember as well that the prefix for a namespace is only a shorthand convention, the real element name (Qualified Name, or QName for short) consists of the full namespace and the local name. Changing the prefix for a namespace does not change the 'identity' of an element - if it is in the same namespace and same local name then it is the same kind of element, even if the prefix is different.
XPath 2.0 (or rather XSLT 2.0) has the concept of the 'default xpath namespace'. You can set the xpath-default-namespace attribute on the xsl:stylesheet element.
Using libxml it seems this works:
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);
}
You need local-name():
http://www.w3.org/TR/xpath#function-local-name
To crib from http://web.archive.org/web/20100810142303/http://jcooney.net:80/archive/2005/08/09/6517.aspx:
<foo xmlns='urn:foo'>
<bar>
<asdf/>
</bar>
</foo>
This expression will match the “bar” element:
//*[local-name()='bar']
This one won't:
//bar
If you are trying to use xslt you can add the namespace in to the stylesheet declaration. If you do that, you must make sure that there is a prefix or it will not work. If the source XML does not have a prefix, that is still fine, you add your own prefix in the stylesheet.
Stylesheet
<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>
Or something like that.
I tried something similar to what palehorse proposed and could not get it to work. Since I was getting data from a published service I couldn't change the xml. I ended up using XmlDocument and XmlNamespaceManager like so:
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