XSLT 1.0 Pattern Matching Tips for Source Documents with Namespaces
One of the most frequent gotchas that users of XSLT encounter involves the failure of their xsl:template match patterns to work as they expect when working with a source document containing namespaces. The scenario usually unfolds like this. The developer has a source document like this:
<Foo xmlns="uri:something"> <Bar baz="bop"/> </Foo>
and an XSLT stylesheet like this:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> : <xsl:template match="Foo/Bar[@baz='bop']">
and what they observe is that their xsl:template doesn't match as expected.
The problem occurs is due to the presence of an XML namespace in the source XML file, and the lack of a presence of a namespace in the XPath expressions in the XSLT template's match patterns.
If you have an XML document with no namespace like:
<Foo> <Bar baz="bop"/> </Foo>
Then your XSLT match patterns can match the <Bar> element as a child of the <Foo> element having an attribute named 'baz' with a value of 'bop' using a match pattern like this:
<xsl:template match="Foo/Bar[@baz='bop']">
However, if your source XML document uses namespaces like this:
<n:Foo xmlns:n="uri:something"> <n:Bar baz="bop"/> </n:Foo>
Then a match pattern of:
<xsl:template match="Foo/Bar[@baz='bop']">
will not match because in XPath 1.0, an unqualified element in a match pattern only matches elements with no namespace URI at all.
So, you would need to change your XSLT stylesheet to define the namespace prefix that matches the same namespace URI you need to match, and then explicitly qualify the elements in your match pattern like this:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:n="uri:something"> : <xsl:template match="n:Foo/n:Bar[@baz='bop']">
The not-so-obvious thing occurs when the XML souce document is using namespaces, but taking the syntactic shortcut of using the so-called "default namespace" like this:
<Foo xmlns="uri:something"> <Bar baz="bop"/> </Foo>
Syntactically it looks like there are no namespaces in use, however the default namespace rules imply that the document above is semantically identical to the one with the explicit "n:" namespace prefix above, since both the explicit namespace prefix "n:" and the implicit, default namespace above map to the same namespace URI string "uri:something".
So, even in the case immediately above with the default namespace, you need your stylesheet to define a namespace prefix and explicitly reference that namespace prefix in the match pattern like this:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:n="uri:something"> : <xsl:template match="n:Foo/n:Bar[@baz='bop']">
Note, that the namespace prefix you choose is actually completely irrelevant here. It's just a syntactic stand-in for the real meangingful part, which is the namespace URI. So, it wouldn't matter if I used the namespace prefix "n" or "steve", so long as it maps to the namespace URI of "uri:something" that I want to match. Therefore, you can also write the above stylesheet like this:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:steve="uri:something"> : <xsl:template match="steve:Foo/steve:Bar[@baz='bop']">
Note that unlike elements, attributes do NOT inherit the default namespace, so that is why with the document:
<Foo xmlns="uri:something"> <Bar baz="bop"/> </Foo>
The match pattern needs to be "steve:Foo/steve:Bar[@baz='bop]" with the explicitly qualified element names, but an unqualified attribute name.
On the other hand, if the attribute name were explicitly namespace-qualified like this:
<Foo xmlns="uri:something" xmlns:z="uri:something"> <Bar z:baz="bop"/> </Foo>
Or semantically equivalently...
<z:Foo xmlns:z="uri:something"> <z:Bar z:baz="bop"/> </z:Foo>
Then you would need to also explicitly qualify the attribute in the match pattern using any convenient namespace prefix that maps to the same namespace URI "uri:something" you want to match:
<xsl:template match="steve:Foo/steve:Bar[@steve:baz='bop']">
|