XML.com 
 Published on XML.com http://www.xml.com/pub/a/2001/05/02/trxml.html
See this if you're having trouble printing code examples

 

Namespaces and Stylesheet Logic
By Bob DuCharme
May 02, 2001

In last month's "Transforming XML" column, we saw how to control the namespaces that are declared and referenced in your result document. If your stylesheet needs to know details about which namespaces are used in your source document, and to perform tasks based on which namespaces certain elements or attributes belong to, XSLT offers a variety of ways to find out. This month we look at them.

To experiment, we'll use the following document. It has one title element and one verse element from the http://www.snee.com/red namespace, two verse elements from the http://www.snee.com/blue namespace, and one verse element from the default namespace.

<poem xmlns:red="http://www.snee.com/red"
         xmlns:blue="http://www.snee.com/blue">
<red:title>From Book IV</red:title>
<blue:verse>The way he went, and on the Assyrian mount</blue:verse>
<red:verse>Saw him disfigured, more then could befall</red:verse>
<blue:verse>Spirit of happy sort: his gestures fierce</blue:verse>
<verse>He marked and mad demeanor, then alone</verse>
</poem>

Sample documents and stylesheets are available in this zip file. Our first stylesheet has template rules that act on element nodes based on various conditions. Each adds a text node to the result tree about what it found (for example, "Found a red node:") followed by information about the node it found. Note that for the http://www.snee.com/blue namespace, the stylesheet uses a prefix that is different from the one that the document above uses — instead of "blue", it uses the German word for "blue": "blau".

<!-- xq255.xsl: converts xq254.xml into xq256.txt -->
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                xmlns:red="http://www.snee.com/red"
                xmlns:blau="http://www.snee.com/blue"
                version="1.0">
<xsl:output method="text"/>

  <xsl:template match="poem">
   Namespace nodes:
    <xsl:for-each select="namespace::*"> 
      <xsl:value-of select="name()"/><xsl:text> </xsl:text>
    </xsl:for-each>
    <xsl:apply-templates/>
  </xsl:template>

  <xsl:template match="blau:verse">
   Found a blue verse.
   name          <xsl:value-of select="name()"/>
   local-name    <xsl:value-of select="local-name()"/>
   namespace-uri <xsl:value-of select="namespace-uri()"/>
   contents      <xsl:apply-templates/>
  </xsl:template>

  <xsl:template match="red:*">
   Found a red node:
   name          <xsl:value-of select="name(.)"/>
   local-name    <xsl:value-of select="local-name(.)"/>
   namespace-uri <xsl:value-of select="namespace-uri(.)"/>
   contents      <xsl:apply-templates/>
  </xsl:template>

  <xsl:template match="verse">
   Found a verse element from the default namespace:
   name          <xsl:value-of select="name(.)"/>
   local-name    <xsl:value-of select="local-name(.)"/>
   namespace-uri <xsl:value-of select="namespace-uri(.)"/>
   contents      <xsl:apply-templates/>
  </xsl:template>

  <xsl:template match="*"/>

</xsl:stylesheet>

Let's look at the result of applying this stylesheet to the document above, before we talk about how it does what it does:

   
Namespace nodes:
    xml blue red 

   Found a red node:
   name          red:title
   local-name    title
   namespace-uri http://www.snee.com/red
   contents      From Book IV

   Found a blue verse.
   name          blue:verse
   local-name    verse
   namespace-uri http://www.snee.com/blue
   contents      The way he went, and on the Assyrian mount

   Found a red node:
   name          red:verse
   local-name    verse
   namespace-uri http://www.snee.com/red
   contents      Saw him disfigured, more then could befall

   Found a blue verse.
   name          blue:verse
   local-name    verse
   namespace-uri http://www.snee.com/blue
   contents      Spirit of happy sort: his gestures fierce

   Found a verse element from the default namespace:
   name          verse
   local-name    verse
   namespace-uri 
   contents      He marked and mad demeanor, then alone

When the first template rule finds a poem element, it lists all the namespace nodes for that element. It does this by using an xsl:for-each instruction to count through the names in the namespace axis with any name ("*"), adding each one's name to the result tree by calling the name() function in an xsl:value-of instruction's select attribute. It then puts a single space after each name with an xsl:text element, so that they don't run together. In addition to the "blue" and "red" namespaces declared in the poem element's start-tag, note the "xml" namespace that starts the list in the result; an XSLT processor assumes that this was implicitly declared. (It's supposed to, but they don't all actually do so.)

The second template rule looks for verse elements in the "blau" namespace. Remember, "blau" isn't really the namespace name — as we can see in the xsl:stylesheet start-tag, it's the prefix assigned in the stylesheet to refer to the namespace that's really called http://www.snee.com/blue. The sample source document has two verse elements from the http://www.snee.com/blue namespace, and even though the stylesheet and source documents refer to this namespace with different prefixes, they're still referring to the same namespace -- so the XSLT processor recognizes them and adds two "Found a blue verse" text nodes to the result tree.

Each of these result tree sections has four lines to tell us about the element node that the template processed:

    

Also in Transforming XML

Automating Stylesheet Creation

Appreciating Libxslt

Push, Pull, Next!

Seeking Equality

The Path of Control

The stylesheet's third template rule looks for any element in the "red" namespace and adds the same information to the result tree that the blue:verse template rule added. Because the source document included both a title element and a verse element from the http://www.snee.com/red namespace, both get a four-line report in the result. Their corresponding element type names show up in their "name" and "local-name" parts of the result tree. The stylesheet's final template rule suppresses any elements not accounted for in the first three template rules.

We've seen how a template can select all the elements with a specific name from a specific namespace (in the example above, the verse elements from the http://www.snee.com/blue namespace) and how it can select all the elements, regardless of their names, from a particular namespace (in the example, those from the http://www.snee.com/red namespace). The next template shows how to select all the elements of a particular name regardless of their namespace: it has a match pattern for all the verse elements from any namespace.

<!-- xq257.xsl: converts xq254.xml into xq258.txt -->

  <xsl:template match="*[local-name()='verse']">
    Found a verse:
    name          <xsl:value-of select="name()"/>
    local-name    <xsl:value-of select="local-name()"/>
    namespace-uri <xsl:value-of select="namespace-uri()"/>
    contents      <xsl:apply-templates/>
  </xsl:template>

Technically speaking, it's really matching all the elements for which the local part of their name is "verse". This match pattern uses it to look for elements of any name ("*") that meet the condition in the predicate: the local-name() function must return a value of "verse". When we apply this stylesheet to the document used in the earlier examples, the result shows two verse elements from the "blue" namespace, one from the "red" namespace, and one from the default namespace (that is, one with no specific namespace assigned to it—the last verse element in the source document).

    Found a verse:
    name          blue:verse
    local-name    verse
    namespace-uri http://www.snee.com/blue
    contents      The way he went, and on the Assyrian mount

    Found a verse:
    name          red:verse
    local-name    verse
    namespace-uri http://www.snee.com/red
    contents      Saw him disfigured, more then could befall

    Found a verse:
    name          blue:verse
    local-name    verse
    namespace-uri http://www.snee.com/blue
    contents      Spirit of happy sort: his gestures fierce

    Found a verse:
    name          verse
    local-name    verse
    namespace-uri 
    contents      He marked and mad demeanor, then alone

For a more realistic example, we'll convert certain elements of an XLink document, regardless of their element names, to HTML. The first template rule in the following stylesheet applies to elements with any name ("*") that meet both of the two conditions in the predicate:

<!-- xq259.xsl: converts xq260.xml into xq261.html -->
<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="html"/>

  <xsl:template match="*[@xlink:type = 'simple' and @xlink:href]">
   <a href="{@xlink:href}"><xsl:apply-templates/></a>
  </xsl:template>

  <xsl:template match="recipe">
   <html><body>
    <xsl:apply-templates/>
   </body></html>
  </xsl:template>

</xsl:stylesheet>

If both of these conditions are true, the element—regardless of its element name—gets converted to an HTML a element in the result tree with the source tree XLink element's href attribute value used for the HTML href attribute in the result tree version. The template rule will do this to both the author and ingredient elements of the following document:

<recipe xmlns:xlink="http://www.w3.org/1999/xlink">
  <author xlink:href="http:/www.jcookie.com"
          xlink:type="simple">Joe "Cookie" Jones</author>
  <ingredients>
    <ingredient xlink:href="http:/www.snee.com/food/flour.html"
                xlink:type="simple">flour</ingredient>
    <ingredient xlink:href="http:/www.snee.com/food/sugar.html"
                xlink:type="simple">sugar</ingredient>
  </ingredients>
  <steps/>
</recipe>

Because of the example's simplicity, the result won't look fancy in a browser, but it does demonstrate how the two different element types can both be converted to HTML a elements with one template rule because of the namespace of their attributes:

<html>
   <body>
      <a href="http:/www.jcookie.com">Joe "Cookie" Jones</a>
      
      <a href="http:/www.snee.com/food/flour.html">flour</a>
      <a href="http:/www.snee.com/food/sugar.html">sugar</a>
      
      
      
   </body>
</html>

It also demonstrates the use of the exclude-result-prefixes attribute in the xsl:stylesheet element that I mentioned in last month's column. This attribute tells the XSLT processor to keep the original elements' namespace declaration and prefixes out of the result, which helps to make the result something that any web browser would understand.

XSLT's ability to base processing logic on namespace values makes it a great tool for developing XLink applications. As you use XSLT to process more and more XML documents with elements from specialized namespaces—for example, SOAP envelopes, XHTML elements, or XSL formatting objects—you'll find these techniques invaluable for keeping track of which elements come from where so that you can take the appropriate action with them.

XML.com Copyright © 1998-2006 O'Reilly Media, Inc.