102 – XML Namespaces

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>