XML Namespaces are used to specify and separate vocabularies within XML documents. Although they are not required, it is common for generated XML to include, at a minimum, a default namespace definition in the root node. Atom feeds, for instance, open with <feed xmlns="http://www.w3.org/2005/Atom">
, and then often define more namespaces specific to the feed’s subject matter. The addition of namespaces means that seemingly simple queries, like select="/feed"
, return nothing, because <feed>
and every node below it are effectively hidden in a different namespace.
You can, of course, write a stylesheet that accounts for namespaces as it selects data and composes its output. This is demonstrated below, using [xslt_transform_xml/]
in the final “Stylesheet Equivalent” sample for each section. The other samples use [xslt_select_xml/]
.
There are four (4) sections below, starting with the baseline case – XML with no (0) namespace info. The following section addresses a single (1) namespace in the XML, and starts adding parameters to [xslt_select_xml/]
. The final sections use two (2) namespaces in the XML, but defines them through two (2) different methods.
0 namespaces
<root> <node>ONE</node> <node>TWO</node> </root>
Sample One – //node
Without namespaces, [xslt_select_xml/]
only requires a simple XPath expression to select any node or value in the data. With select="//node"
we are requesting all <node>
elements in the document, regardless of their position in the XML.
[xslt_select_xml xml="namespaces-0-xml" select="//node" /]
<RESULT xml="namespaces-0-xml" id="1029" select="//node"> <node>ONE</node> <node>TWO</node> </RESULT>
Stylesheet Equivalent – //node
[xslt_transform_xml xml="namespaces-0-xml" xsl="namespaces-0-xsl" /]
<RESULT xml="namespaces-0-xml" xsl="namespaces-0-xsl" select="//node"> <node>ONE</node> <node>TWO</node> </RESULT>
namespaces-0-xsl
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"> <xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/> <xsl:param name="xml"/> <xsl:param name="xsl"/> <xsl:template match="/"> <RESULT> <xsl:attribute name="xml"><xsl:value-of select="$xml"/></xsl:attribute> <xsl:attribute name="xsl"><xsl:value-of select="$xsl"/></xsl:attribute> <xsl:attribute name="select">//node</xsl:attribute> <xsl:copy-of select="//node"/> </RESULT> </xsl:template> </xsl:stylesheet>
1 namespace, set in root node
<root xmlns="uri:namespace-one"> <node>ONE</node> <node>TWO</node> </root>
Sample One – //node
After adding a namespace to the XML, select="//node"
no longer works. Instead it returns an empty <RESULT>
.
[xslt_select_xml xml="namespaces-1-xml" select="//node" /]
<RESULT xml="namespaces-1-xml" id="1030" select="//node"/>
Sample Two – //node + strip-namespaces
To ignore the new namespace and get the original selection working again, we can add the strip-namespaces="yes"
option to the shortcode. The output should be equivalent to Sample One for 0 namespaces.
[xslt_select_xml xml="namespaces-1-xml" strip-namespaces select="//node" /]
<RESULT xml="namespaces-1-xml" id="1030" select="//node"> <node>ONE</node> <node>TWO</node> </RESULT>
Sample Three – //ns1:node + xmlns + ns1
The recommended approach to selecting data with namespaces is, of course, to specify the required information inside [xslt_select_xml/]
. After adding xmlns
and, in this case, the corresponding ns1
attribute, only a small change to the select
is required to get data flowing again.
Note: there’s nothing special about the ns1
key used here – you can choose any name – but the URI value for that key must match the URI that appears in the XML.
[xslt_select_xml xml="namespaces-1-xml" xmlns="ns1" ns1="uri:namespace-one" select="//ns1:node" /]
<RESULT xml="namespaces-1-xml" id="1030" select="//ns1:node"> <node xmlns="uri:namespace-one">ONE</node> <node xmlns="uri:namespace-one">TWO</node> </RESULT>
Stylesheet Equivalent – //ns1:node
[xslt_transform_xml xml="namespaces-1-xml" xsl="namespaces-1-xsl" /]
<RESULT xml="namespaces-1-xml" xsl="namespaces-1-xsl" select="//ns1:node"> <node xmlns="uri:namespace-one">ONE</node> <node xmlns="uri:namespace-one">TWO</node> </RESULT>
namespaces-1-xsl
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:ns1="uri:namespace-one" version="1.0" exclude-result-prefixes="ns1"> <xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/> <xsl:template match="/"> <RESULT> <xsl:attribute name="xml"><xsl:value-of select="$xml"/></xsl:attribute> <xsl:attribute name="xsl"><xsl:value-of select="$xsl"/></xsl:attribute> <xsl:attribute name="select">//ns1:node</xsl:attribute> <xsl:copy-of select="//ns1:node"/> </RESULT> </xsl:template> </xsl:stylesheet>
2 namespaces, second set in sub-node
<root xmlns="uri:namespace-one"> <node>ONE</node> <node xmlns="uri:namespace-two">TWO</node> </root>
Sample One – //ns1:node + xmlns + ns1
After adding a second namespace, the selection we used just above (in Sample Three for 1 namespace) continues to work. It returns nodes from the first namespace as before, but nodes from the second namespace are now hidden.
[xslt_select_xml xml="namespaces-2-xml" xmlns="ns1" ns1="uri:namespace-one" select="//ns1:node" /]
<RESULT xml="namespaces-2-xml" id="1031" select="//ns1:node"> <node xmlns="uri:namespace-one">ONE</node> </RESULT>
Sample Two – //ns2:node + xmlns + ns2
To get a node directly from the second namespace, we can change the URI supplied to [xslt_select_xml/]
.
[xslt_select_xml xml="namespaces-2-xml" xmlns="ns2" ns2="uri:namespace-two" select="//ns2:node" /]
<RESULT xml="namespaces-2-xml" id="1031" select="//ns2:node"> <node xmlns="uri:namespace-two">TWO</node> </RESULT>
Sample Three – /ns1:root/ns2:node + xmlns + ns1 + ns2
And finally, we can specify both namespaces in [xslt_select_xml/]
, use both prefixes in the XPath expression, and again select any node or value in the XML. Here, we fetch <node>TWO<node>
, like Sample Two, but this time with reference to <root>
, which belongs to the first namespace.
[xslt_select_xml xml="namespaces-2-xml" xmlns="ns1 ns2" ns1="uri:namespace-one" ns2="uri:namespace-two" select="/ns1:root/ns2:node" /]
<RESULT xml="namespaces-2-xml" id="1031" select="/ns1:root/ns2:node"> <node xmlns="uri:namespace-two">TWO</node> </RESULT>
Stylesheet Equivalent – /ns1:root/ns2:node
[xslt_transform_xml xml="namespaces-2-xml" xsl="namespaces-2-xsl" /]
<RESULT xml="namespaces-2-xml" xsl="namespaces-2-xsl" select="/ns1:root/ns2:node"> <node xmlns="uri:namespace-two">TWO</node> </RESULT>
namespaces-2-xsl
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:ns1="uri:namespace-one" xmlns:ns2="uri:namespace-two" version="1.0" exclude-result-prefixes="ns1 ns2"> <xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/> <xsl:template match="/"> <RESULT> <xsl:attribute name="xml"><xsl:value-of select="$xml"/></xsl:attribute> <xsl:attribute name="xsl"><xsl:value-of select="$xsl"/></xsl:attribute> <xsl:attribute name="select">/ns1:root/ns2:node</xsl:attribute> <xsl:copy-of select="/ns1:root/ns2:node"/> </RESULT> </xsl:template> </xsl:stylesheet>
2 namespaces, second set in root node with prefix
<root xmlns="uri:namespace-one" xmlns:two="uri:namespace-two"> <node>ONE</node> <two:node>TWO</two:node> </root>
Sample One – //ns1:node + xmlns + ns1
Sample One here matches Sample one in the previous section. Although the second namespace definition moved to <root>
, the results are fundamentally the same. The difference is in the namespace information included in the result. Now, because both namespaces are defined in the root – both above our selected node – the result includes details for both.
[xslt_select_xml xml="namespaces-3-xml" xmlns="ns1" ns1="uri:namespace-one" select="//ns1:node" /]
<RESULT xml="namespaces-3-xml" id="1032" select="//ns1:node"> <node xmlns="uri:namespace-one" xmlns:two="uri:namespace-two">ONE</node> </RESULT>
Sample Two – //ns2:node + xmlns + ns2
Sample Two here matches Sample Two in the previous section, and again the result matches except for namespace info.
[xslt_select_xml xml="namespaces-3-xml" xmlns="ns2" ns2="uri:namespace-two" select="//ns2:node" /]
<RESULT xml="namespaces-3-xml" id="1032" select="//ns2:node"> <two:node xmlns="uri:namespace-one" xmlns:two="uri:namespace-two">TWO</two:node> </RESULT>
Sample Three – /ns1:root/ns2:node + xmlns + ns1 + ns2
And finally, matching the previous Sample Three, we can specify both namespaces in [xslt_select_xml/]
, use both prefixes in the XPath expression, and select any node or value in the XML. Once again, we’ll fetch <node>TWO<node>
.
[xslt_select_xml xml="namespaces-3-xml" xmlns="ns1 ns2" ns1="uri:namespace-one" ns2="uri:namespace-two" select="/ns1:root/ns2:node" /]
<RESULT xml="namespaces-3-xml" id="1032" select="/ns1:root/ns2:node"> <two:node xmlns="uri:namespace-one" xmlns:two="uri:namespace-two">TWO</two:node> </RESULT>
Stylesheet Equivalent – /ns1:root/ns2:node
[xslt_transform_xml xml="namespaces-3-xml" xsl="namespaces-3-xsl" /]
<RESULT xml="namespaces-3-xml" xsl="namespaces-3-xsl" select="/ns1:root/ns2:node"> <two:node xmlns="uri:namespace-one" xmlns:two="uri:namespace-two">TWO</two:node> </RESULT>
namespaces-3-xsl
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:ns1="uri:namespace-one" xmlns:ns2="uri:namespace-two" version="1.0" exclude-result-prefixes="ns1 ns2"> <xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/> <xsl:template match="/"> <RESULT> <xsl:attribute name="xml"><xsl:value-of select="$xml"/></xsl:attribute> <xsl:attribute name="xsl"><xsl:value-of select="$xsl"/></xsl:attribute> <xsl:attribute name="select">/ns1:root/ns2:node</xsl:attribute> <xsl:copy-of select="/ns1:root/ns2:node"/> </RESULT> </xsl:template> </xsl:stylesheet>