Namespaces in XSLT

This is part 2 of the “XML Namespaces” chapter I wrote for Advanced XML Applications from the Experts at The XML Guild (published by Thomson). See also: Part 1: Understanding XML Namespaces.

Now that we’ve covered what namespaces are for and how they work, let’s take a look at how they are handled in XPath and XSLT. Namespace concerns in XSLT can be broadly divided into two categories:

  • Dealing with namespaces in the input document, or processing namespaces; and
  • Dealing with namespaces in the output document, or constructing namespaces.

In the next two sections, we’ll cover each of these in turn.

Table of Contents

Processing Namespaces

This section concerns how namespaces are represented in the XPath data model and how the XPath language allows you to access the information in that data model. In this section, when we say “XPath,” we’re talking about both the 1.0 and 2.0 versions of the language. Where we need to make a distinction, we’ll say “XPath 1.0” or “XPath 2.0”. Also, unless we’re talking specifically about XSLT features, all of the XPath observations in this section are also applicable to XQuery, which is an extension of XPath 2.0.

See “XQuery 1.0: An XML Query Language” at http://www.w3.org/TR/xquery/.

Selecting elements and attributes by name

In XPath, to select an element named foo, you (unsurprisingly) use the name test foo. A namespace-qualified element can be selected with an expression such as xyz:foo. For this to work without error, the XPath context must be initialized to include a binding between the xyz prefix and a non-empty namespace URI. Different XPath APIs have their own ways of initializing expression context. But when XSLT is the host language, the namespace context is determined by the namespace bindings that are in scope where the XPath expression appears in the XSLT document. We saw an example of this earlier in the chapter, in the "QNames in Content" section, where the x prefix was bound to the XHTML namespace.

The key thing to remember is that what prefix is (or isn’t) used in the source document is completely independent of what prefix you use in your stylesheet. When selecting an element or attribute by name, only its local name and namespace URI are considered.

XSLT 2.0: xpath-default-namespace

In XPath 1.0, an unprefixed name in an expression always means “not in a namespace”. For example, the expression foo always means “select all child elements that have local name foo and that are not in a namespace”. In other words, the default namespace is not used for XPath 1.0 expressions. If you want to select a namespace-qualified element or attribute, you must use a prefix (and include a corresponding namespace declaration for that prefix).

This turns out to be quite a pain if one day you decide to start using namespaces when you haven’t been using them previously. First, you update all your input documents. This could be as simple as manually typing in a default namespace at the top of your files:

<doc xmlns="http://example.com">
...

Next, you update all your stylesheets. Because your stylesheets were designed to process non-namespace-qualified elements, none of your XPath expressions use namespace prefixes. And because you can’t declare a default namespace for XPath 1.0 expressions, that means you’ll have to go through and update each and every XPath expression so that it uses namespace prefixes.

Fortunately, XPath 2.0 provides a remedy; it allows you to apply a default namespace to unprefixed element names in expressions. Now, to update your stylesheets, you won’t have to touch every XPath expression. Provided that you’re using XSLT 2.0, all you have to do is add the xpath-default-namespace attribute on the document element of each stylesheet:

<xsl:stylesheet version="2.0"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  xpath-default-namespace="http://example.com">
...

Now, a bare foo expression in this stylesheet will only select elements in the http://example.com namespace. Note that an XPath default namespace, like a default namespace in XML itself, applies only to elements names, not attribute names.

Accessing element and attribute names

XPath defines the local name and namespace URI as properties of the element or attribute node itself and provides functions for accessing those properties. Specifically, the local-name() function returns the local part of the expanded-name, and the namespace-uri() function returns the universal part.

For example, consider this document:

<my:doc xmlns:my="http://example.com" id="abc123"/>

Table 1.1 shows what’s returned by these functions for the <my:doc> element and the id attribute.

Table 1.1: The local-name() and namespace-uri() functions
Expression Value returned (string)
local-name(/*) doc
namespace-uri(/*) http://example.com
local-name(/*/@id) id
namespace-uri(/*/@id) (empty string)

Notice that the prefix my doesn’t appear anywhere in the above table. That’s because XPath does not directly expose the prefix that’s used in an element or attribute name. The only way you can get to that is by way of the name() function, which we’ll take a look at later, in Perils of the name() function.

Name wildcards

Name tests in XPath 1.0 can be grouped into three categories. These are shown in Table 1.2. These examples assume that x is bound to http://example.com.

Table 1.2: Name tests in XPath 1.0
Description Name test Equivalent to
Match any name * *
Match a particular expanded-name x:foo *[local-name() = 'foo' and namespace-uri() = 'http://example.com']
Match a particular namespace x:* *[namespace-uri() = 'http://example.com']
XPath 2.0: namespace wildcards

XPath 2.0 adds an additional category, shown in Table 1.3.

Table 1.3: Syntax for namespace wildcards
Description Name test Equivalent to
Match a particular local name *:foo *[local-name() = 'foo']

The *:foo expression matches any element whose local name is foo, regardless of its namespace URI. This syntax is only legal in XPath 2.0. In XPath 1.0, you must instead use *[local-name() = 'foo'].

Namespace nodes

Consider the following XML document:

<foo xmlns="http://example.com"
     xmlns:my="http://xmlportfolio.com/xmlguild-examples">
  <bar>
    <bat/>
  </bar>
</foo>

A naive XPath user might try getting this document’s default namespace URI by directly accessing the namespace declaration attribute using an expression like this: //@xmlns. But this will never return a result, because namespace declarations are not exposed as attributes in the XPath data model. Even though namespace declarations use XML 1.0’s syntax for attributes, they’re not attributes as far as XPath is concerned.

So if namespace declarations aren’t attributes, what are they? There’s actually no direct representation for namespace declarations in the XPath data model. Instead, each element has a set of one or more namespace nodes attached to it that represents the set of namespace prefix/URI bindings that are in scope for that element. Even if you don’t use namespaces, each element will at least have the implicit xml namespace node attached to it.

Given that knowledge, how many namespace nodes do you think are in the above document? Two? Three? The answer is: nine. Each of the three elements has three namespace nodes attached to it: a default namespace, the xml namespace, and the namespace bound to the prefix my. This becomes really clear when we realize that an exactly equivalent representation of the above document can be expressed using nine namespace declarations:

<foo xmlns="http://example.com"
     xmlns:my="http://xmlportfolio.com/xmlguild-examples"
     xmlns:xml="http://www.w3.org/XML/1998/namespace">
  <bar xmlns="http://example.com"
       xmlns:my="http://xmlportfolio.com/xmlguild-examples"
       xmlns:xml="http://www.w3.org/XML/1998/namespace">
    <bat xmlns="http://example.com"
         xmlns:my="http://xmlportfolio.com/xmlguild-examples"
         xmlns:xml="http://www.w3.org/XML/1998/namespace"/>
  </bar>
</foo>

This is more like what XPath sees when it looks at the document. It gets past the syntax sugar that allows us to type a namespace declaration once on an ancestor element. Instead, for each element, XPath sees a namespace node for each in-scope namespace, not unlike what we see in this much more verbose (but perfectly valid) serialization.

Like other XPath node types, each namespace node has an expanded-name and a string-value. The name of a namespace node is the prefix, and the string-value is the URI that the prefix is bound to. (The namespace URI part of a namespace node’s name is always empty.) Table 1.4 lists the value of these properties for the three namespace nodes that appear on each element above:

Table 1.4: Namespace node properties
expanded-name string-value
local part namespace URI part
xml [always empty] http://www.w3.org/XML/1998/namespace
empty [always empty] http://example.com
my [always empty] http://xmlportfolio.com/xmlguild-examples

The namespace axis

To access namespace nodes in XPath, you use the namespace axis. For example, this expression will select the namespace node attached to the document element that binds the my prefix.

/*/namespace::my

And this expression returns the string-value of the node, that is, the namespace URI that’s bound to the my prefix:

string(/*/namespace::my)

In practice, it’s usually not necessary to access namespace nodes directly like this, unless you’re performing some sort of namespace surgery on the document. The most common use case is creating additional namespaces in the result document. We’ll see an example of this later, in XSLT 1.0: Copying namespace nodes using the namespace axis.

One example of such surgery is the “Namespace declaration normalizer” stylesheet I wrote several years ago.

Perils of the name() function

In addition to the local-name() and namespace-uri() functions, XPath provides the name() function. The name() function returns a QName string that represents the expanded-name of the given argument node with respect to the namespace declarations that were in effect on the argument node in the source document. In regard to XPath 2.0’s name() function, we can use a simpler description: it returns the lexical QName used for the element or attribute name in the source document. In XPath 1.0, the name() function usually will behave the same, but it could return a different QName than the one that’s used in the document. The easiest way to explain this distinction is by example. Consider the following document, which has two namespace declarations for the same namespace URI:

<my:foo xmlns:my="http://example.com" xmlns:my2="http://example.com"/>

In XPath 2.0, name(/*) will always return the string my:foo. In XPath 1.0, it could return either my:foo or my2:foo. That’s because the XPath 1.0 data model doesn’t include the actual prefix that’s used on an element or attribute node. To find an appropriate prefix, the applicable namespace nodes must be queried. In this case, there are two choices, so it could use either one. That said, XPath 1.0 implementations are allowed to preserve what QName is actually used, even though it’s not part of the data model, and always return it. The difference in XPath 2.0 is that the prefix of an element or attribute now is part of the data model and the name() function is required to use that prefix in its result.

Ultimately, this is pretty inconsequential, because it turns out you should avoid using the name() function most of the time anyway. Recall that earlier in this chapter, in “Misusing the namespace context,” we saw how dependence on the use of a particular namespace prefix was a bad thing. Well, in XSLT, here’s how you’d make the same mistake. The following is a naïve but legal way of matching an atom:feed element:

<xsl:template match="*[name() = 'atom:feed']">
  <!-- ... -->
</xsl:template>

This will work as long as your documents use the string atom as the namespace prefix. But it won’t work, for example, for the Atom documents we looked at earlier in this chapter, which instead used a default namespace (no prefix). To ensure that your template rule works for all Atom documents, you’ll want to write it this way instead:

<xsl:template match="atom:feed">
  <!-- ... -->
</xsl:template>

Then you would just need to declare xmlns:atom="http://www.w3.org/2005/Atom" at the top of your document so that the XSLT processor knows what namespace you’re referring to when you use the atom prefix. At that point, what prefix you to choose to use is a mere detail of how you wrote your stylesheet, not an external contract that requires people to use a particular prefix. The only requirement is that input documents use the correct namespace URI.

In most cases, you should avoid using the name() function and instead use local-name() and namespace-uri() to access the two parts of an expanded name. But I can think of a few cases where using the name() function is okay:

  • When you’re inserting diagnostic or debugging information in the result about which element was matched, via <xsl:value-of select="name(.)"/>.
  • When the argument node is a node other than an element or attribute, in which case the result of name() will always be the same as the result of local-name().
  • When you’re testing for xml:space or xml:preserve, as in @*[name() != 'xml:space']. This is safe because the xml prefix is fixed 1:1 to that namespace.
  • When you’re testing for an attribute that is not in a namespace, as in @*[name() != 'id']. This is safe because an unprefixed attribute name always means “not in a namespace”. That’s because the default namespace is not in effect for attribute nodes.
  • When you’re creating a node using <xsl:element name="{name(.)}" namespace="{namespace-uri(.)}">. This is safe (as is an analogous use of <xsl:attribute>), because a prefix that might be present in the name attribute won’t be used to look up the namespace URI as long as you provide it explicitly using the namespace attribute. The reason this usage of name() is handy is that the XSLT processor can then use that prefix in the element name that it creates in the result tree. (In XSLT 1.0, using the prefix is optional. In XSLT 2.0, it’s required, barring any conflicts.)

I’ll leave it up to you to decide whether to try and remember these cases or just follow the blanket rule of using local-name() instead.

Constructing Namespaces

We’ve seen how to deal with namespaces on the input side, using XPath. Now we’ll take a look at how to control namespaces on the output side, using XSLT. Your choices for controlling output namespaces in XSLT 1.0 were fairly limited. Much was left up for the implementer to decide. XSLT 2.0 introduces several new features designed to transcend those limitations and give more control to the stylesheet writer.

This section takes up a good portion of the chapter, and many of the XSLT 2.0 features I’ll introduce here were designed for some admittedly arcane use cases. But the only way to be truly ready for a real problem when it arises is to have a comprehensive understanding upfront. Besides, there’s a certain satisfaction that comes from being able to control exactly what namespace declarations appear in your result document.

Namespace nodes: a mostly covert operation

Most of the time, in XSLT, you will not directly cross paths with namespace nodes. They just get copied along with the element nodes that you copy from the source tree or stylesheet. We saw an example of this behavior earlier, in the “Un-declaring Namespaces” section. When either the <xsl:copy-of> or <xsl:copy> instruction is applied to an element node, it copies all of the element’s namespace nodes along with it, thereby ensuring that the same namespace bindings are in scope in the result tree as were in scope for that element in the source tree.

Similarly, whenever you use a literal result element in your stylesheet, it is treated as an instruction to copy that element from the stylesheet into the result tree. In that case also, the namespace nodes are copied along with that element into the result tree.

Finally, whenever you create an element or attribute using the explicit XSLT instructions for doing so (<xsl:element> and <xsl:attribute>), the XSLT processor will automatically create the necessary namespace nodes in the result tree (and corresponding namespace declarations in the serialized result) to ensure that the result is namespace-well-formed.

The bottom line is that when you’re just using namespaces for the purpose of qualifying element and attribute names—regardless of how you create the element or attribute in the result, you don’t have to do anything special to ensure that the appropriate namespace nodes will be generated. The XSLT processor takes care of this for you.

Just to drive this point home, imagine you want to start using namespaces in your documents. If there are only a few documents to update, then you could just open the files in a text editor and manually add an xmlns declaration to the top of each one. But what if you have a thousand files to update? XSLT can do the trick, but in XSLT you approach the problem much differently than you would if you were editing the files manually. That’s because the xmlns declaration is not an attribute as far as XPath and XSLT are concerned. Instead, it’s a shorthand way of qualifying all the element names in the document. Accordingly, the essence of your task is this: update all the element names in the document. The stylesheet for doing that looks like this:

<xsl:stylesheet version="1.0"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

  <!-- copy these nodes as is -->
  <xsl:template match="@* | comment() | processing-instruction()">
    <xsl:copy/>
  </xsl:template>

  <!-- but rename the elements -->
  <xsl:template match="*">
    <xsl:element name="{local-name()}"
                 namespace="http://example.com/new-namespace">
      <xsl:apply-templates select="@* | node()"/>
    </xsl:element>
  </xsl:template>

</xsl:stylesheet>

The task is thus a matter of naming elements. You don’t have to worry about namespace nodes; they are automatically created as a by-product of the names you give your elements.

So when do you have to worry about namespace nodes? Broadly speaking, there are two scenarios where namespace nodes will appear on your radar screen:

  • The result has too many namespaces, or
  • The result doesn’t have enough namespaces.

In the first case, the result has extraneous namespace declarations that you don’t need, because you’re not using any QNames in content. In the second case, the result doesn’t include enough namespace context to expand all the QNames that you’re using in character data or attribute values. Again, in both of these cases, we’re not talking about namespace declarations that are needed to expand element and attribute names. Those will always be present, and you can’t (nor would you want to) prevent them from appearing.

Too many namespaces

There are generally two situations in which you might end up with more namespace nodes in the result than you want or need:

  • When copying an element from the stylesheet, that is, when using a literal result element, and
  • When copying an element from the source tree, using <xsl:copy> or <xsl:copy-of>.

In the first case (literal result elements), there is one relevant feature available in both XSLT 1.0 and 2.0 to help filter undesired namespaces from the result: the exclude-result-prefixes attribute.

In the second case (copying elements from the source tree), XSLT 2.0 introduces a new feature to help filter undesired namespaces from the result: the copy-namespaces attribute, which is an optional attribute on the <xsl:copy> and <xsl:copy-of> instructions.

A third feature, also introduced in XSLT 2.0, is the inherit-namespaces attribute, which allows you to disable XSLT’s default behavior of automatically copying namespace nodes to descendant elements in the result tree.

Let’s take a more detailed look at each of these features.

XSLT 1.0: exclude-result-prefixes

The following stylesheet uses a literal result element to create the <html> document element in the result tree:

<xsl:stylesheet version="1.0"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  xmlns:my="http://example.com">

  <xsl:template match="/*">
    <html>
      <xsl:apply-templates select="my:section"/>
    </html>
  </xsl:template>

  <!-- ... -->

</xsl:stylesheet>

We include the xmlns:my namespace declaration in order to be able to select nodes in that namespace from the input document, using XPath expressions like my:section. But this declaration has another effect that we might not want. Since the my namespace is in scope on the <html> literal result element, the XSLT processor will copy that namespace node along with the <html> element into the result tree. Without any further changes to the stylesheet, the result will look like this:

<html xmlns:my="http://example.com">
  <!-- ... -->
</html>

Fortunately, XSLT (both 1.0 and 2.0) provides an easy way to avoid this. If we have no use for the my namespace in the result, then we can use the exclude-result-prefixes attribute to list what namespaces we want excluded from the result:

<xsl:stylesheet version="1.0"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  xmlns:my="http://example.com"
  exclude-result-prefixes="my">
...

Now our result looks like this:

<html>
  <!-- ... -->
</html>

Note that we didn’t have to explicitly include the xsl namespace in that list. It’s the one exception for literal result elements: the XSLT namespace is not automatically copied to the result. If for some reason you need the XSLT namespace in the result (and if you aren’t already generating an element or attribute in the XSLT namespace), then you would have to explicitly copy a namespace node for the XSLT namespace. We’ll see an example of how to copy namespace nodes in the Not enough namespaces section.

In addition to the exclude-result-prefixes attribute, any namespaces listed in the extension-element-prefixes attribute will also be excluded from the result (again, provided they’re not necessary to qualify element or attribute names).

XSLT 2.0: copy-namespaces

Consider the following input document:

<doc xmlns:my="http://example.com" my:id="AAA">
  <p>This is the first paragraph.</p>
  <p>This is the second paragraph.</p>
</doc>

The only purpose of the my namespace in this case is to qualify the my:id attribute. The elements are not in a namespace. Now, let’s say we want to copy the paragraphs from this document into the result tree. We’ll start with a stylesheet like this:

<xsl:stylesheet version="1.0"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

  <xsl:template match="/doc">
    <new-doc>
      <xsl:apply-templates/>
    </new-doc>
  </xsl:template>

  <xsl:template match="p">
    <xsl:copy>
      <xsl:apply-templates/>
    </xsl:copy>
  </xsl:template>

</xsl:stylesheet>

Here’s the result of the transformation:

<new-doc>
  <p xmlns:my="http://example.com">This is the first paragraph.</p>
  <p xmlns:my="http://example.com">This is the second paragraph.</p>
</new-doc>

As you can see, the result is cluttered with namespace nodes from the original document. That’s because the default behavior of <xsl:copy> (and <xsl:copy-of>) when copying elements is to copy all the namespace nodes along with that element. In XSLT 1.0, this is always the case. (You might think that exclude-result-prefixes could be used, but that only applies to literal result elements in the stylesheet, not to elements that are copied from the source tree.)

XSLT 2.0, however, provides a new copy-namespaces attribute which can be used to prevent unnecessary namespace nodes from being copied into the result. Its default value is yes. If we’re using an XSLT 2.0 processor, then we can override the default behavior like this:

  <xsl:template match="p">
    <xsl:copy copy-namespaces="no">
      <xsl:apply-templates/>
    </xsl:copy>
  </xsl:template>

Now the result looks more pristine:

<new-doc>
  <p>This is the first paragraph.</p>
  <p>This is the second paragraph.</p>
</new-doc>
XSLT 1.0: Dodging unwanted namespace nodes

Even though XSLT 1.0 doesn’t have the copy-namespaces attribute, there are other ways to get the result that you want. One way is to just use a literal result element instead of <xsl:copy>, like this:

  <xsl:template match="p">
    <p>
      <xsl:apply-templates/>
    </p>
  </xsl:template>

A more generic approach (which could be extended to match more than just <p> elements) uses the <xsl:element> instruction to “replicate” rather than copy the element, using the same local name and namespace URI:

  <xsl:template match="*">
    <xsl:element name="{name()}" namespace="{namespace-uri()}">
      <xsl:apply-templates/>
    </xsl:element>
  </xsl:template>

The above rule ensures that the resulting element will have the same local name and namespace URI as the context element in the source tree, but it won’t end up with any unnecessary namespaces, because it’s not actually copying the element from the source tree.

XSLT 2.0: inherit-namespaces and undeclare-prefixes

Recall our XInclude example from earlier, in the “Un-declaring Namespaces” section. XSLT 2.0 introduces a couple of new features that allow us to effectively implement an XInclude processor in XSLT, complete with support for XML 1.1 and namespace undeclarations on the output side.

Let’s augment our original XInclude example slightly. Here we’ve added an additional, unused default namespace at the top of the document:

<my:doc xmlns:my="http://xmlportfolio.com/xmlguild-examples"
        xmlns="http://example.com">
<xi:include href="doc2.xml"
            xmlns:xi="http://www.w3.org/2001/XInclude"/>
</my:doc>

And let’s say the content of doc2.xml does use a namespace now:

<s:simple xmlns:s="http://example.com/simple">
  <s:remark>We *do* use namespaces.</s:remark>
</s:simple>

In XSLT 1.0 or 2.0, a simple XInclude implementation might look like this:

<xsl:stylesheet version="2.0"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  xmlns:xi="http://www.w3.org/2001/XInclude">

  <!-- by default, copy all nodes -->
  <xsl:template match="@* | node()">
    <xsl:copy>
      <xsl:apply-templates select="@* | node()"/>
    </xsl:copy>
  </xsl:template>

  <!-- but replace XInclude elements with the referenced content -->
  <xsl:template match="xi:include">
    <xsl:apply-templates select="document(@href)"/>
  </xsl:template>

</xsl:stylesheet>

Here’s the result of applying that stylesheet to our original document:

<my:doc xmlns:my="http://xmlportfolio.com/xmlguild-examples"
        xmlns="http://example.com">
<s:simple xmlns:s="http://example.com/simple">
  <s:remark>We *do* use namespaces.</s:remark>
</s:simple>
</my:doc>

The inclusion worked fine, but the included document is now muddied with namespace nodes inherited from the <my:doc> ancestor. Extracting the contents of doc2.xml again later would yield a cluttered result:

<s:simple xmlns:s="http://example.com/simple"
          xmlns:my="http://xmlportfolio.com/xmlguild-examples"
          xmlns="http://example.com">
  <s:remark>We *do* use namespaces.</s:remark>
</s:simple>

To avoid this, we need a way to prevent those namespace nodes from being inherited. In other words, we want to prevent them from being automatically copied to all descendant elements when the document inclusion takes place. XSLT 2.0 provides the inherit-namespaces attribute for just that purpose. Its default value is yes, so we want to change it to no. It can appear on <xsl:element>, <xsl:copy>, or any literal result element. (When attached to a literal result element, it must use the XSLT namespace; in that case it would be named xsl:inherit-namespaces.) In our case, we want to use it on the <xsl:copy> element, like this:

  <!-- by default, copy all nodes -->
  <xsl:template match="@* | node()">
    <xsl:copy inherit-namespaces="no">
      <xsl:apply-templates select="@* | node()"/>
    </xsl:copy>
  </xsl:template>

This has the effect that, when this template rule is invoked for the <my:doc> element, the XSLT processor is instructed to not copy the result element’s namespace nodes to its descendant elements, which is otherwise the default behavior. (Unfortunately, inherit-namespaces is a bit of a misnomer. If I pass something down to my descendants, I’m not inheriting anything; they are. It might better have been called propagate-namespaces, or even more fun: bequeath-namespaces. But as of this writing, the XSLT 2.0 recommendation is on its way out the door, so that’s not going to change. My current way of coping with this is to think of inherit-namespaces not so much as a property of the element, but instead as an imperative that’s issued by the element: “My dear children and grandchildren, inherit from me!”)

If we reapply the stylesheet, things look better, but the serialized result is still not quite what we wanted:

<my:doc xmlns:my="http://xmlportfolio.com/xmlguild-examples"
        xmlns="http://example.com">
<s:simple xmlns:s="http://example.com/simple" xmlns="">
  <s:remark>We *do* use namespaces.</s:remark>
</s:simple>
</my:doc>

Specifically, while the default namespace was undeclared (using xmlns=""), the my namespace was not. We want to see xmlns:my="" in there too. But this is a feature that’s only available in XML 1.1. That’s the problem. We need to tell the XSLT processor that we want XML 1.1 as output. Also, since namespace undeclarations (other than for the default namespace) are such a newfangled thing, we need to explicitly tell the processor that, yes, we want those in the serialized result too. At the top level of our stylesheet (as a child of the <xsl:stylesheet> element), we must make one more addition:

<xsl:output version="1.1" undeclare-prefixes="yes"/>

If we apply our updated stylesheet one last time, we now get the result we wanted:

<?xml version="1.1"?>
<my:doc xmlns:my="http://xmlportfolio.com/xmlguild-examples"
        xmlns="http://example.com">
<s:simple xmlns:s="http://example.com/simple" xmlns:my="" xmlns="">
  <s:remark>We *do* use namespaces.</s:remark>
</s:simple>
</my:doc>

Now, both the my namespace and the default namespace are explicitly disabled on the included <s:simple> element, which is to say that those namespace nodes are not inherited. The upshot is that, when we go to extract the <s:simple> element again, we’ll get back exactly the same data as was in the original doc2.xml file, including the same namespace nodes—no more, no less.

It turns out that including the <xsl:output> declaration as shown above is the only way to ensure that an XML 1.1 document can be fully round-tripped through XSLT. Specifically, undeclare-prefixes="yes" ensures that not only will the presence of namespace nodes be preserved, but so will their absence.

Not enough namespaces

The opposite situation (not enough namespace declarations in the result) is not nearly as common a problem. It can happen though. For example, if your result document uses QNames in content or attribute values, then you need to make sure that the corresponding namespace declarations for those QNames will appear in the result. Even then, this usually isn’t a problem, because you can rely on the XSLT processor to copy the namespace nodes for you automatically—either from the source document when you use <xsl:copy> or <xsl:copy-of>, or from the stylesheet when you use a literal result element.

So when do you have to go out of your way to get a namespace into the result document? The answer, of course, is when the namespace nodes you need are not copied for you automatically. The most common case of that happening is when you use <xsl:element> to generate the result element. In that case, no additional namespace nodes will be generated other than the one that’s needed to qualify the element name itself (when the element is in a namespace).

There are two ways to explicitly make a namespace node appear in the result:

  • By explicitly copying existing namespace nodes (using XPath’s namespace axis) or, when that’s not possible,
  • By generating a new namespace node, using XSLT 2.0’s <xsl:namespace> instruction.

We’ll now look at examples of both of these techniques.

XSLT 1.0: Copying namespace nodes using the namespace axis

It may be counterintuitive, but sometimes you want to add a namespace node so that your result document will have fewer namespace declarations. Recall the example document we saw earlier in XSLT 2.0: copy-namespaces. Before we added copy-namespaces="no" to our stylesheet, this is the result we were getting:

<new-doc>
  <p xmlns:my="http://example.com">This is the first paragraph.</p>
  <p xmlns:my="http://example.com">This is the second paragraph.</p>
</new-doc>

Let’s say now that we don’t want to strip this namespace from the result, but instead we would just like to have it be declared on the document element instead, so that it doesn’t have to be declared on each and every <p> element, which isn’t so nice-looking. We can do that when we create the <new-doc> element, by explicitly copying all the namespace nodes from the source tree’s <doc> element, like this:

  <xsl:template match="/doc">
    <new-doc>
      <xsl:copy-of select="namespace::*"/>
      <xsl:apply-templates/>
    </new-doc>
  </xsl:template>

This causes the in-scope namespaces of <doc> to also be in-scope for the <new-doc> element. Now the result tree, when serialized, only needs one namespace declaration at the top, which is much nicer-looking:

<new-doc xmlns:my="http://example.com">
  <p>This is the first paragraph.</p>
  <p>This is the second paragraph.</p>
</new-doc>

This goes to show you that having more namespace nodes does not necessarily mean having more namespace declarations.

XSLT 2.0: Creating namespace nodes using <xsl:namespace>

XSLT 2.0 filled an obvious gap in the XSLT 1.0 recommendation: the inability to create an arbitrary namespace node. Of course, this wasn’t usually a problem for people, and even when you did need to dynamically create a namespace node, there were workarounds using an extension function, as we’ll see.

Let’s say we have a bunch of W3C XML Schema documents lying around that were generated from an automated tool. And let’s say that the automated tool forgot to output a default namespace declaration, thereby breaking all the QName references to internal schema components. (Unlike XSLT, W3C XML Schemas do use the default namespace to expand QNames in attribute values.) To fix all the schemas in bulk, we need to add an xmlns declaration to the top of each document and set its value to the same as that of the schema’s targetNamespace attribute.

In other words, we need to turn this:

<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"
            targetNamespace="http://www.example.com">
...

Into this:

<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"
            targetNamespace="http://www.example.com"
            xmlns="http://example.com">
...

This example may seem a bit contrived, but by gum it meets the criteria for a use case for <xsl:namespace>. We need to add a namespace node that’s not already there, it won’t be generated automatically for us, and we don’t know what the namespace URI will be until run-time. If we already knew what namespace URI we needed to add to each document, we could just hard-code a corresponding namespace declaration into the stylesheet or secondary input document and copy it from there using the document() function and the namespace:: axis. (The advantage of that approach is that it’s possible to do in XSLT 1.0 without extensions.) But since we don’t know what the namespace URI is going to be, we can’t hard-code a namespace declaration anywhere; we must create it dynamically. That’s what <xsl:namespace> is for.

Here’s a stylesheet that does just what we need:

<xsl:stylesheet version="2.0"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

  <!-- Add a namespace node to the document element -->
  <xsl:template match="/*">
    <xsl:copy>
      <xsl:namespace name="" select="string(@targetNamespace)"/>
      <xsl:copy-of select="@* | node()"/>
    </xsl:copy>
  </xsl:template>

</xsl:stylesheet>

The name attribute determines what prefix to use. Since this one’s empty, it means a default namespace node will be created. The select expression ensures that the targetNamespace value gets used as the namespace URI of the resulting namespace node.

We could have written this stylesheet a bit differently. For example, we could have explicitly created a namespace node for every element in the document (not just the document element). After all, we do need the namespace to be in scope for every element. But that approach would produce the exact same result. As you might recall from our discussion of the inherit-namespaces attribute, XSLT’s default behavior is to copy all namespace nodes to descendant elements in the result tree. (In other words, the default value of inherit-namespaces is yes.) As long as we insert the namespace node into the document element, it will automatically get copied to the rest of the elements in the document.

XSLT 1.0: Creating dynamic namespace nodes

Even though the <xsl:namespace> instruction is not available in XSLT 1.0, there are still ways to create dynamic namespace nodes, provided you’re willing to use your processor’s version of the node-set() extension function (for converting result tree fragments to first-class node-sets). Fortunately, most XSLT 1.0 processors provide such an extension. Not so fortunately, this approach isn’t entirely reliable, because implementers aren’t as constrained as they are in XSLT 2.0 to use the exact namespace prefix that you specify.

In any case, the trick is to create a temporary element whose namespace is determined at run-time. This causes a namespace node to be created, which you can then access and copy into the result tree. Below is an XSLT 1.0 solution to the schema fix-up problem we introduced in the last section. We’re using the EXSLT project’s node-set() extension, which is implemented in several popular processors, including Saxon and libxslt:

<xsl:stylesheet version="1.0"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  xmlns:exsl="http://exslt.org/common">

  <xsl:template match="/*">
    <xsl:variable name="ns-uri" select="string(@targetNamespace)"/>
    <xsl:variable name="ns-container">
      <xsl:element name="dummy" namespace="{$ns-uri}"/>
    </xsl:variable>
    <xsl:copy>
      <xsl:copy-of select="exsl:node-set($ns-container)
                           /*/namespace::*[. = $ns-uri]"/>
      <xsl:copy-of select="@* | node()"/>
    </xsl:copy>
  </xsl:template>

</xsl:stylesheet>

This stylesheet works perfectly in Saxon. Unfortunately it doesn’t do so well in libxslt. While perfectly conformant, libxslt autogenerates a prefix when you don’t specify one in the name attribute of <xsl:element>. The result has an xmlns:ns1 declaration instead of the xmlns declaration that we were hoping for. Saxon, on the other hand, takes the absence of a prefix to mean that we want to generate a default namespace declaration, which in fact is what we want. Both implementations are correct according to the XSLT 1.0 recommendation. Only with XSLT 2.0’s <xsl:namespace> instruction do we have full assurance that the namespace node that’s created will use exactly the prefix (or absence of a prefix) that we specify.

Generating XSLT using xsl:namespace-alias

The final namespace-related use case we’ll be considering is the use of “meta-stylesheets”, that is, using XSLT to generate XSLT. Below is a naïve attempt at using XSLT to generate another stylesheet:

<xsl:stylesheet version="1.0"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

  <xsl:template match="/">
    <xsl:stylesheet version="1.0"> <!-- wrong -->
      ...
    </xsl:stylesheet>
  </xsl:template>

</xsl:stylesheet>

The presumed intention here was to generate an xsl:stylesheet element in the result, but the XSLT processor will just choke when it sees that and say, “Hey, that’s not allowed here.” That’s because it has no way of knowing that you want <xsl:stylesheet> copied to the result, rather than interpreted as an XSLT instruction. One way to get around this is to use the <xsl:element> instruction instead:

  <xsl:template match="/">
    <xsl:element name="xsl:stylesheet">
      <xsl:attribute name="version">1.0</xsl:attribute>
      ...
    </xsl:element>
  </xsl:template>

Now it’s clear that you want to create an element in the result. Problem solved. The only nagging thing is that this stylesheet is going to start looking pretty verbose pretty fast if you’re going to have to use <xsl:element> for every element (and consequently <xsl:attribute> for every attribute). This is where <xsl:namespace-alias> comes to the rescue. It lets you define a “dummy” namespace whose only purpose is to temporarily disambiguate between literal result elements and stylesheet instructions. Here’s what it looks like:

<xsl:stylesheet version="1.0"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  xmlns:out="http://xmlportfolio.com/dummy">

  <xsl:namespace-alias stylesheet-prefix="out" result-prefix="xsl"/>

  <xsl:template match="/">
    <out:stylesheet version="1.0">
      ...
    </out:stylesheet>
  </xsl:template>

</xsl:stylesheet>

This is much nicer. Now all we have to do is use the xsl prefix for stylesheet instructions that we want executed now, and the out prefix for literal result elements—in other words, code that we want to generate. The <xsl:namespace-alias> element instructs the XSLT processor, after it’s done generating the result tree, to convert all elements (and attributes) in the dummy out namespace to the actual XSLT namespace. Effectively, the resulting document will use the correct XSLT namespace.

Go back to “Part 1: Understanding XML Namespaces”

Contact: [email protected]; +1 (206) 898-1654
Copyright © 2024 — Lenz Consulting Group, Inc.