XML.com: XML From the Inside Out
oreilly.comSafari Bookshelf.Conferences.

advertisement

Namespaces and XSLT Stylesheets

April 04, 2001

In XML a namespace is a collection of names used for elements and attributes. A URI (usually, a URL) is used to identify a particular collection of names. Instead of including the URI with every element to show which namespace it came from, it's more convenient to name a short abbreviation when a namespace is declared and to then use that abbreviation to identify an element or attribute's namespace.

For example, in the document shown in the figure below, the table element explicitly declares that it's from the HTML 4.0 namespace by putting the appropriate URI in an xmlns attribute. This makes this namespace the default for everything inside of this element as well -- in other words, an XML processor will treat all of its contents (the tr elements and their contents) as elements from the HTML namespace unless they have namespace declarations or prefixes from other namespaces.

A document referencing two namespaces

A document referencing two namespaces

The author element has two attributes that reference a namespace declared in the article element's start-tag. It shows this by adding the "xlink" prefix declared with the http://www.w3.org/1999/xlink namespace to those attribute names. An XLink-aware application will find those type and href attributes and know what to do with them.

Many simple XML applications never need to declare and use namespaces. If they don't, the XML processor treats all elements and attributes as being in the default namespace. This may be the case with some of your source documents, but it's certainly not the case with your XSLT stylesheets: the declaring and referencing of the XSLT namespace is how we tell an XSLT processor which elements and attributes in a stylesheet to treat as XSLT instructions. For example, the document element's start-tag in the following stylesheet declares a namespace for the http://www.w3.org/1999/XSL/Transform URI and assigns the prefix xsl to represent it.

<!-- xq236.xsl: converts xq237.xml into xq238.xml -->

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

  <xsl:output method="xml" omit-xml-declaration="yes"/>

  <xsl:template match="xdata">
    <heading>xdata</heading><xsl:text>
    </xsl:text>
   <text><xsl:apply-templates/></text>
  </xsl:template>

</xsl:stylesheet>

(Sample documents and stylesheets are available in this zip file.) When this stylesheet's single template rule sees an xdata element in the source tree, it adds two elements to the result tree: a heading element with the string "xdata" as its contents and a text element with the source tree xdata element's contents as its contents. It turns this

<xdata>With Opal Towers and Battlements adorned</xdata>
into this:

<heading>xdata</heading>
<text>With Opal Towers and Battlements adorned</text>

It also adds a carriage return between the result tree's heading and text elements. It does this with an xsl:text element that has a carriage return as its contents. The XSLT processor knows the difference between the text element that has the carriage return and the text element with the xsl:apply-templates instruction as its contents because the one with the carriage return has that xsl: prefix to show that it's part of the http://www.w3.org/1999/XSL/Transform namespace. The second of these two text elements has no prefix, so the XSLT processor doesn't treat it as a special XSLT element -- it's just another literal result element.

The following stylesheet, which assigns and uses harpo as the prefix for the XSLT namespace, works identically to the one above:

<!-- xq239.xsl: converts xq237.xml into xq238.xml -->

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

  <harpo:output method="xml" omit-xml-declaration="yes"/>

  <harpo:template match="xdata">
    <heading>xdata</heading><harpo:text>
    </harpo:text>
   <text><harpo:apply-templates/></text>
  </harpo:template>

</harpo:stylesheet>

Some XSLT processors support special instructions known as "extensions" that are not part of the W3C XSLT specification. To use these instructions, you declare the namespace mentioned in that processor's documentation and then refer to those instructions using whatever prefix you declared for that namespace.

Namespaces and Your Result Document

What if you want your result document to include elements from a specified namespace? Simply declare and use the namespace you need in your stylesheet and the XSLT processor will

  • put that namespace declaration in the start-tag of the result document's document element, and

  • put the prefix for that namespace in the tags for any result tree elements from that namespace.

(If you want it to include elements from the http://www.w3.org/1999/XSL/Transform namespace -- that is, if you want your result document to be an XSLT stylesheet -- it's a little trickier than the general case; we'll cover it below.)

For example, to convert the following HTML document to an XLink document, the result needs an XLink namespace declaration and each of the special XLink attributes need a prefix that references that declaration.

<html><body>
<p>The poem's <a href="jmilton.html">author</a> was English.</p>
</body></html>

The stylesheet declares the http://www.w3.org/1999/xlink namespace in the xsl:stylesheet element's start-tag along with the declaration of the http://www.w3.org/1999/XSL/Transform namespace.

<!-- xq242.xsl: converts xq241.html into xq243.xml -->

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                xmlns:xlink="http://www.w3.org/1999/xlink"
                version="1.0">
<xsl:output method="xml" omit-xml-declaration="yes"/>

<xsl:template match="a">
  <author xlink:type="simple" xlink:href="{@href}">
    <xsl:apply-templates/></author>
</xsl:template>

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

</xsl:stylesheet>

You'll usually find a namespace declaration in a document element's start-tag, and the XSLT processor passes this declaration along to the start-tag of the result document's document element. It also passes all references to that namespace along to the result tree, making the result document a working XLink document.

<para xmlns:xlink="http://www.w3.org/1999/xlink">
The poem's <author xlink:type="simple" xlink:href="jmilton.html">
author</author> was English.</para>

In XSLT terms, the XSLT processor has added a namespace node to the result tree. (There are six kinds of nodes that may show up in source and result trees: elements, attributes, text nodes, processing instructions, comments, and namespace nodes.) You can prevent this from happening with the xsl:stylesheet element's exclude-result-prefixes attribute. This attribute's name can be confusing, because the namespace prefixes will still show up in the result tree. It doesn't mean "exclude the prefixes in the result"; it means "exclude the namespaces with these prefixes".

For example, the following stylesheet looks just like the one above with the addition of this attribute to tell the XSLT processor to exclude the namespace node represented by the "xlink" prefix. You can list multiple namespace prefixes as the value of the exclude-result-prefixes attribute, as long as they have spaces between them and they're all declared namespaces.

<!-- xq244.xsl: converts xq241.html into xq245.xml -->

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                xmlns:xlink="http://www.w3.org/1999/xlink"
                exclude-result-prefixes="xlink"
                version="1.0">
  <xsl:output method="xml" omit-xml-declaration="yes"/>

  <xsl:template match="a">
   <author xlink:type="simple" xlink:href="{@href}">
    <xsl:apply-templates/></author>
  </xsl:template>

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

</xsl:stylesheet>

Processing the same source document with this stylesheet creates a similar result, except that the document element's start-tag doesn't have the declaration for the XLink namespace:

<para>
The poem's <author xlink:type="simple" xlink:href="jmilton.html">
author</author> was English.</para>

You can also assign result tree elements and attributes to specific namespaces by adding a namespace attribute to the xsl:element instruction or to an xsl:attribute instruction. For example, the second xsl:element instruction in the following template and the two xsl:attribute elements inside of the first xsl:element each include a namespace attribute along with their name attributes. These identify the two namespaces where the element and attribute names belong: the HTML and XLink namespaces.

<!-- xq246.xsl: converts xq237.xml into xq247.xml -->

<xsl:template match="xdata">

  <section>

    <xsl:element name="author">
      <xsl:attribute namespace="http://www.w3.org/1999/xlink"
        name="type" >simple</xsl:attribute>
      <xsl:attribute namespace="http://www.w3.org/1999/xlink"
        name="href" >jmilton.html</xsl:attribute>
      John Milton
    </xsl:element>

    <xsl:element name="img" 
      namespace="http://www.w3.org/TR/REC-html40">
      <xsl:attribute name="src">milton.jpg</xsl:attribute>
    </xsl:element>
    <xsl:apply-templates/>

  </section>

</xsl:template>

When applied to the same source document as the earlier examples, this stylesheet creates a result document that has an author element with two attributes from the XLink namespace and an img element from the HTML namespace. The XSLT processor can make up any namespace prefixes it wants; in this case, they're "ns0" and "ns1".

<section>
<author xmlns:ns0="http://www.w3.org/1999/xlink" 
        ns0:type="simple" ns0:href="jmilton.html">
      John Milton
    </author>
<ns1:img xmlns:ns1="http://www.w3.org/TR/REC-html40"
     src="milton.jpg"/>
With Opal Towers and Battlements adorned</section>

To review: the XSLT processor looks through a stylesheet for elements belonging to the XSLT namespace and executes their instructions, and for any elements outside of that namespace (that aren't from a namespace declared for extension elements), it passes them along to the result with namespace declarations if necessary. You can even assign a specific namespace for an element or attribute by adding a namespace attribute to an xsl:element or xsl:attribute element.

Using XSLT to Output XSLT

If the elements from the XSLT namespace don't get passed through to the result document, what do you do if you really want XSLT elements in your result element? For example, what if we want to convert the following document to a working XSLT stylesheet?

<ssheet>

  <elementHandler element="para">
    <p><elementContents/></p>
  </elementHandler>

</ssheet>

You use the xsl:namespace-alias element. If you use another temporary namespace (in the example below, "xslAlt") in place of the one you really want (below, "xsl") xsl:namespace-alias lets you tell the XSLT processor to substitute the one you want for the temporary one when it creates the result tree.

  <!-- xq249.xsl: converts xq248.xml into xq250.xsl -->
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                 xmlns:xslAlt="http://www.snee.com/xml/dummy"
     version="1.0">

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

  <xsl:template match="elementHandler">
    <xslAlt:template match="{@element}">
      <xsl:apply-templates/>
    </xslAlt:template>
  </xsl:template>

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

  <xsl:template match="ssheet">
    <xslAlt:stylesheet  version="1.0">
      <xsl:apply-templates/>
    </xslAlt:stylesheet>
  </xsl:template>

  <!-- Just copy any other elements, attributes, etc. -->
  <xsl:template match="@*|node()">
    <xsl:copy>
      <xsl:apply-templates select="@*|node()"/>
    </xsl:copy>
  </xsl:template>

</xsl:stylesheet>

This stylesheet's first template rule will add an xslAlt:template element to the result tree each time it finds an elementHandler element source tree. Before this template, however, the stylesheet's xsl:namespace-alias instruction tells the XSLT processor to substitute the "xsl" prefix's namespace for the "xslAlt" prefix's namespace in the result document, so that the result tree has a namespace declaration assigning "xslAlt" as a prefix for the http://www.w3.org/1999/XSL/Transform namespace. This is the XSLT namespace, so the result document is a valid XSLT stylesheet:

<?xml version="1.0" encoding="utf-8"?>
<xslAlt:stylesheet 
   xmlns:xslAlt="http://www.w3.org/1999/XSL/Transform" 
   xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">

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

</xslAlt:stylesheet>

When applied to this source document,

<para>Fallen cherub, to be weak is miserable</para>
the stylesheet created by the transformation with the xsl:namespace-alias element creates this result:

<?xml version="1.0" encoding="UTF-8"?>
<p>Fallen cherub, to be weak is miserable</p>

Creating XSLT instructions in your result document is only the most obvious application of the xsl:namespace-alias element. Whenever you have elements that you don't want recognized as belonging to a certain namespace until later in the process, xsl:namespace-alias can put the prefix you want on those elements. Or, it can take them out -- a result-prefix value of "#default" assigns the relevant result-tree elements to the default namespace, which means they get no namespace prefix at all.

In this column, we've seen how to control the namespaces that are declared and referenced in your result document. Next month, we'll see how your XSLT stylesheet can check which namespaces are used in your source document and perform tasks based on which namespace each element belongs to.