The Subtleties of XPath namespacing

Typically when writing xml documents, you would tend to define a default namespace.

However, XPath has no concept of a “default” for namespaces. Instead it looks for the expression in the “null” namespace….something quite different.

<?xml version="1.0" encoding="utf-8"?>
<root xmlns="http://example.com/xml" xmlns:foo="http://example.com/xml/foo">
    <node id="1" />
    <foo:node id="2" />
    <node xmlns="" id="2" />
</root>

What are the distinctions here? There are three above: default namespace, declared namespace (foo:), and the null namespace (the last tag uses a null namespace).

<?xml version="1.0" encoding="utf-8"?>
<root>
    <node id="1" />
    <node id="2" />
    <node id="3" />
</root>

This code above here uses a null namespace for all tags.

XPath statements are reading these nodes with an assumption that all undeclared node names are from the “null namespace”.

Statements like this would work for the 2nd example:

my @nodes = $doc->findnodes('//node');

However, if applied to that first example, you would only get one match (), instead of the expected two (since the obvious non-match would be the tag using the document’s declared namespace.

This breaks down however, you cannot match on default namespace…you need to declare it as well.

You can only access null namespaces or declared namespaces from XPath. You cannot access defined default namespaces (unless it is defined as null).

my @nodes = $doc->findnodes('//foo:node');

Occasionally, this is just too much namespace madness to deal with — the easiest solution is to remove the default namespace from your xml (keep in mind if you’re intermixing xml, then this isn’t a good idea).


#PHP Example assuming the following xml snippet:

<?xml version="1.0" encoding="utf-8"?>
<root xmlns="http://example.com/xml" xmlns:foo="http://example.com/xml/foo">
    <node id="1" />
    <foo:node id="2" />
    <node xmlns="" id="2" />
</root>

...

$doc = new DOMDocument();
$doc->load($xml_filename);
$doc->childNodes->item(0)->removeAttributeNS('http://example.com/xml', '');

# Now you can use XPATH from the null namespace...and named namespace "foo"
# example.xsl

<xsl:template match="root/node">
...# matches first and 3rd node tags.
</xsl:template>

<xsl:template match="//foo:node">
... # matches 2nd node tag: <foo:node>
</xsl:template>

VN:F [1.8.4_1055]
Rating: 0.0/10 (0 votes cast)
VN:F [1.8.4_1055]
Rating: 0 (from 0 votes)

Leave a Reply