Skip to main content

If you don't have an IBM ID and password, register here.

By clicking Submit, you agree to the developerWorks terms of use.

The first time you sign into developerWorks, a profile is created for you. This profile includes the first name, last name, and display name you identified when you registered with developerWorks. Select information in your developerWorks profile is displayed to the public, but you may edit the information at any time. Your first name, last name (unless you choose to hide them), and display name will accompany the content that you post.

All information submitted is secure.

The first time you sign in to developerWorks, a profile is created for you, so you need to choose a display name. Your display name accompanies the content you post on developerworks.

Please choose a display name between 3-31 characters. Your display name must be unique in the developerWorks community and should not be your email address for privacy reasons.

By clicking Submit, you agree to the developerWorks terms of use.

All information submitted is secure.

Planning to upgrade XSLT 1.0 to 2.0, Part 5: Make your stylesheets work with any processor version

Examples of stylesheets that run with XSLT 1.0 and 2.0

David Marston, Software Engineer, IBM, Software Group
David Marston has worked with XML technologies since late 1998, particularly on standards conformance. Over his 25+ years in the computing business, he has been involved with all aspects of software development. He is a graduate of Dartmouth College and a member of the ACM. He is on the Next-Generation Web team at IBM Research. You can contact him at David_Marston@us.ibm.com.
Joanne Tong, Software Developer, IBM, Software Group
Joanne Tong is a developer working on IBM's XSLT processors in the IBM Toronto lab. She is currently an editor of the W3C XSLT 2.0 and XQuery 1.0 Serialization specification and is an active member of the XSL working group. You can contact her at joannet@ca.ibm.com.

Summary:  This article provides examples of stylesheets that are portable between versions 1.0 and 2.0, with special guidance for those who must run both 1.0 and 2.0 processors for a long transition period. The new 2.0 features may occur in the form of instruction elements, declaration elements, XPath operators, functions, or new attributes or children on elements that existed in 1.0. For each form of enhancement, only certain techniques apply. To read the other articles in this series, go to the Planning to upgrade overview page.

View more content in this series

Date:  27 Feb 2007
Level:  Intermediate

Comments:  

About this series

XSLT 2.0, the latest specification released by the W3C, is a language for transforming XML documents. It includes numerous new features, with some specifically designed to address shortcomings in XSLT 1.0. In this collection of articles, you'll get a high-level overview and an in-depth look at XSLT 2.0 from the point of view of an XSLT 1.0 user who wants to fix old problems, learn new techniques, and discover what to look out for. Examples derived from common applications and practical suggestions are provided if you wish to upgrade. To help you begin to use XSLT 2.0, migration techniques will be provided.


For those who can't abandon XSLT 1.0 processors right away

This article presents the details of mixing versions 1.0 and 2.0 in an XSLT stylesheet. You will learn why version 2.0 elements aren't necessarily fatal to a 1.0 processor and how to segregate old and new code where they must differ.

XSLT 1.0, which you should be familiar with, was designed with the knowledge that future versions would be specified. The most essential evidence of this fact is the requirement that all stylesheets have a version attribute on the outer xsl:stylesheet element. All XSLT processors, including 1.0 processors, are required to support Forwards Compatibility (FC), which is where the version number on the stylesheet is higher than the version of the processor and the processor can still interpret the stylesheet. When FC is in effect on a stylesheet, the processor must be more lax about unknown attributes on XSLT declarations and instructions. The processor must also ignore new XSLT elements at the declaration level without raising an error. Note that the 2.0 specs have subtle differences in their notion of FC, so always read the spec that matches the processor version to get the correct expectations.

An XSLT 2.0 processor must support FC. It might not support Backwards Compatibility (BC), in which case any occurrence of version="1.0" in the stylesheet causes an error, except on xsl:output (where it sets the version of XML). Part 2 of this series presented some decision factors regarding a wholesale switch to 2.0; if you must retain a 1.0 processor in a production use, then this article will show you useful techniques for the 1.0 side.


Portability between versions

In this section, each example has either two or three separate code branches, only one of which any given XSLT processor would execute. Every tool in the portability toolkit is illustrated.

Selecting the execution path must be done in a portable way

All the techniques in the toolkit for cross-version portability (see Part 4 of this series) have some variation in how they will be treated by 1.0 and 2.0 processors. Because these techniques, or guards, act as the dividing point, you must consider how each processor version will be act (or not) upon them if you want your stylesheet to work with processors of both versions. The @use-when attribute is a prime example of a good guard, but it is not available for 1.0 processors to use. This article gives examples which show that a 1.0 processor can still tolerate it in FC mode, so you can use the guard by assuming that a 2.0 (and higher) processor will act on it, while the 1.0 processor will do whatever it would do if the @use-when weren't there.

Another example is the system-property() function, which is known and available in both versions, but has more keywords available as arguments in 2.0. If a 1.0 processor will run the code containing this function, you must limit yourself to keywords that were known in 1.0. Specifically, the xsl:version keyword is the only property known in 1.0 that pertains to processor versions.

If you intend to have a 1.0 processor execute some instructions where the 2.0 processor does nothing, you can combine the two guards just described: use-when="number(system-property('xsl:version')) < 2.0" actually works on all versions! Another one that works, but is less readable, is use-when="not(element-available('xsl:next-match'))" (naming any 2.0-only instruction you would use in an alternative code-block). However, these guards only allow a containing block of 2.0 code to use one of these tests on an element designed to be a 1.0-only island. There is no way to reverse the test (>= or remove the not, respectively) to prevent a 1.0 processor from executing a 2.0-only island.

XSLT also provides the traditional way to select a code execution path using xsl:choose or xsl:if, which can be combined with use-when, system-property(), function-available(), and element-available() to tackle any versioning problems. Similar to @use-when, function-available() in xsl:choose or xsl:if also has a drawback, which is explained in Part 4. Overall, the toolkit is sufficient to cover your needs, though it might take some creativity and a good understanding of both XSLT 1.0 and 2.0 to apply them properly.

Example of code selection for version 1.0, 2.0, and 3.0

You can use the use-when attribute for forwards and backwards compatibility processing. The drawback is that a 1.0 processor does not understand this attribute, whether in FC mode or not, and throws an error if the stylesheet has attribute version = "1.0" (that is, not processed in FC mode). Here is a simple example of the use-when attribute when you are working with a processor that supports a future version of XSLT, say 3.0, and you want your stylesheet to behave differently depending on the version of the processor.


Listing 1. Generic example to do code selection for version 1.0, 2.0, and 3.0 using xsl:choose and @use-when
<xsl:stylesheet version="3.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

<xsl:template match="/">
  <xsl:choose>
    <xsl:when test="number(system-property('xsl:version')) = 1.0">
      <!-- 1.0 instructions -->
    </xsl:when>
    <xsl:otherwise>
      <xsl:sequence use-when="number(system-property('xsl:version')) = 2.0">
        <!-- 2.0 instructions -->
      </xsl:sequence>
      <xsl:sequence use-when="number(system-property('xsl:version')) = 3.0">
        <!-- 3.0 instructions -->
      </xsl:sequence>
    </xsl:otherwise>
  </xsl:choose>
</xsl:template>

</xsl:stylesheet>

Analysis: A 1.0 processor will operate in FC mode (because the stylesheet version is 3.0), but will satisfy the test of the xsl:when clause, and execute that clause. A 2.0 processor will also operate in FC mode, fail the xsl:when test, and go into the xsl:otherwise branch, where the use-when attributes will cause it to see only the content of the first xsl:sequence block. Neither the 1.0 nor 2.0 processor will throw a static error on the higher-version instructions, due to the FC mode. The hypothetical version 3.0 processor will go into the xsl:otherwise branch, where the use-when attributes will cause it to see only the content of the second xsl:sequence block.

Branching at the template level

How can you use the use-when attribute for FC when you work with a 1.0 processor? Here is one way to filter templates with the use-when attribute when you run either a 1.0 or 2.0 processor, and not result in errors even when you have 2.0 constructs that a 1.0 processor does not understand.


Listing 2. Branching at the template level: template/priority filtering
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

<xsl:template use-when="number(system-property('xsl:version')) = 1.0" 
              priority="10" match="/">
  <xsl:text>Running in 1.0</xsl:text>
  <!-- some 1.0 instructions -->
</xsl:template>

<xsl:template use-when="number(system-property('xsl:version')) >= 2.0" 
              priority="9" match="/">
  <xsl:text>Running in </xsl:text><xsl:value-of select="system-property('xsl:version')" />
  <xsl:for-each-group select="nodes">
    <!-- some 2.0 instructions -->
  </xsl:for-each-group>
</xsl:template>

</xsl:stylesheet>

Analysis: This example relies on the fact that a 1.0 processor, with FC processing enabled, ignores the use-when attributes and only runs the first template because it has a higher template priority than the second template (and the same match pattern). For a 2.0 processor, it relies on the XPath expression in the use-when attribute of each template to determine that only the second template is applicable in the transformation. The first template will have disappeared by the time any xsl:apply-templates instruction is executed.

Recommended practice: xsl:import using @use-when and import precedence

This example segregates named templates that achieve equivalent results in 1.0 and 2.0 code. Here is a recommended way to use import precedence, which enables many types of named XSLT elements to override other occurrences having the same name. The import precedence mechanism works the same way in both 1.0 and 2.0 processors, though you can apply it to more named items in 2.0. If two templates have the same name but different import precedence, there is no error and the processor simply chooses the one with higher precedence. The main stylesheet has version="1.0" and avoids XSLT elements that are unknown in XSLT 1.0. It imports imp1. Some elements in imp1 might have the use-when attributes to cause filtering, so imp1 needs to have version="2.0" to trigger FC mode, and suppress errors. All the template content in imp1 is safe for a 1.0 processor. The first import (imp1) imports a second stylesheet module (imp2) that has version="2.0" and has some bits of 2.0-only code that you want to use. Here are excerpts from the main stylesheet module:


Listing 3. Main stylesheet module excerpts
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:import href="imp1"/>

<xsl:template match="/">
    <!-- calls the fit-string template somewhere, but all 1.0 code in this template -->
</xsl:template>

<!-- other 1.0-only templates -->
</xsl:stylesheet>

The stylesheet module it imports, imp1, looks like this in skeletal form:


Listing 4. Skeletal form of imported stylesheet mode
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:import href="imp2"/>

<xsl:template name="fit-string" use-when="number(system-property('xsl:version')) = 1.0">
    <!-- Performs this operation the old and hard way.
      All the code in this template is 1.0 (the legacy code). -->
</xsl:template>

</xsl:stylesheet>

Now imp1 imports imp2, which has an xsl:function declaration, which is new for 2.0, and an alternate version of the named template that uses that function. It looks like this in skeletal form:


Listing 5. Skeletal form of xsl:function
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:my="http://www.example.com/NS/functions" exclude-result-prefixes="my">

<xsl:function name="my:pad">
    <!-- a piece of the task that is best packaged as a function -->
</xsl:function>

<xsl:template name="fit-string" 
              use-when="number(system-property('xsl:version')) >= 2.0">
    <!-- Performs this operation the new and easy way, using my:pad() somewhere.
      There may be other 2.0 code. -->
</xsl:template>

</xsl:stylesheet>

Analysis: A 1.0 processor will perform all the imports, and the version of the fit-string template in imp1 will be assigned higher precedence. A 2.0 processor will also perform both imports, but will act on the use-when attribute in imp1 and remove the fit-string template found there. The 2.0 processor initializes the my:pad function and calls the imp2 version of fit-string, which uses that function.


Testing for availability of functions or elements

One way to guard against an unavailable function or element is to have alternate code paths in the same template. A construct could be deemed unavailable because it's an extension or from a future version of XSLT. However, the notion of an "available" or "extension" function is slightly different between 1.0 and 2.0, so the usual advice about checking specs of the same version as the processor you want to understand bears repeating. If the function you want is unavailable, you need to do something to work around the entire XPath expression containing the call to that function. In the next three examples, the ultimate fallback for unavailable functions is to call a named template. In each example, the result of either the function or named template is captured into a variable, which you can then reference in other XPath expressions.

Choosing between functions and a non-function workaround

Here is a simple example of how to optimize performance with built-in functions when they are supported, and use a named template as a last resort.


Listing 6. Choosing among a built-in 2.0 function, an extension function, and a non-function workaround
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:set="http://exslt.org/sets" extension-element-prefixes="set">
    
  <xsl:template name="makeList">
    <xsl:param name="set" />
    <xsl:variable name="distinctSet">
      <xsl:choose>
        <xsl:when test="number(system-property('xsl:version')) >= 2.0">
          <xsl:sequence select="distinct-values($set)" />
        </xsl:when>
        <xsl:when test="function-available('set:distinct')" 
                  use-when="function-available('set:distinct')">
          <xsl:value-of select="set:distinct($set)" />
        </xsl:when>
        <xsl:otherwise>
          <!-- copy named template from EXSLT website -->
          <xsl:call-template name="set:distinct">
            <xsl:with-param name="nodes" select="$set" />
          </xsl:call-template>
        </xsl:otherwise>
      </xsl:choose>
    </xsl:variable>
    <!-- more instructions -->
  </xsl:template>
  
</xsl:stylesheet>

Analysis: The 1.0 processor runs this in FC mode, ignores all use-when attributes, but always fails the first when test. If it supports the set:distinct function of the EXSLT package (see Resources for a link), it uses it; if not, it is not supposed to throw a static error on a function that it doesn't execute, and it takes the xsl:otherwise branch. A 2.0 processor must implement distinct-values and so it always takes the first branch. Because some 2.0 processors don't support set:distinct, use the @use-when filtering to make it disappear before a static error is raised.

Circumventing an extension not supported in either version

This example applies where you want to use an extension function that has no equivalent built-in function in either XSLT 1.0 or 2.0. The static analysis of the stylesheet, an early-stage check for errors that are independent of the input document, will find errors in all branches of xsl:choose, including unknown elements and unknown XSLT/XPath functions. Unknown extension functions can still be guarded by xsl:choose, but only in BC mode, so it's a good habit to use the @use-when filter at all times. This example has no BC mode, and the use of exslt:power() would be treated as an error during static analysis. But this static analysis occurs after @use-when filtering, and only on the parts that survived the filtering.


Listing 7. Using an extension function that has no equivalent built-in function in either XSLT 1.0 or 2.0

Analysis: All 1.0 processors will run the first template (ignoring @use-when but obeying the higher @priority) and branch on the xsl:choose depending on whether they support exslt:power. A 2.0 processor that supports exslt:power will retain only the first template after @use-when filtering, and will chose the xsl:when branch at run time. A 2.0 processor that does not support exslt:power will retain only the second template after @use-when filtering, and the later phase of static analysis that throws an error about the appearance of an unknown function is only applied to the template that has no mention of it.

Stylesheet function in 2.0 and a workaround in 1.0

Use this example when you have a complex template that you know is better as a function in 2.0, for reasons of readability or performance. Notice that this example has no @use-when but uses function-available() as the filtering criterion.


Listing 8. Changing a complex 1.0 template to a function in 2.0
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:my="http://www.example.org/myFunctions" exclude-result-prefixes="my">

<xsl:function name="my:factorial" as="xs:integer">
  <xsl:param name="num" as="xs:integer" />
  <xsl:value-of select="if ($num > 1) then ($num * my:factorial($num - 1)) else 1" />
</xsl:function>

<xsl:template match="/">
  <xsl:variable name="factorialValue">
    <xsl:choose>
      <xsl:when test="function-available('my:factorial')">
        <xsl:value-of select="my:factorial(5)" />
      </xsl:when>
      <xsl:otherwise>
        <xsl:call-template name="factorial">
          <xsl:with-param name="num" select="5" />
        </xsl:call-template>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:variable>
  <!-- more instructions -->
</xsl:template>

<xsl:template name="factorial">
  <xsl:param name="num" />
  <!-- the usual implementation goes here -->
</xsl:template>

</xsl:stylesheet>

Analysis: The 1.0 processor runs this in FC mode, which allows it to ignore the xsl:function rather than raise an error. The function my:factorial will not be available, so the xsl:otherwise branch will be chosen. In 2.0, the xsl:function definition will succeed, and so the first branch of the xsl:choose will be used. Note: for both versions, the variable takes the form of a temporary tree (known as a Result Tree Fragment in the 1.0 spec), but presumably the subsequent reference to $factorialValue is in a context where the variable is to be atomized down to its numeric content.

Fallback example

In both 1.0 and 2.0, you can nest the xsl:fallback element inside an unknown instruction. (See Part 4 of this series for more explanation of instructions and other XSLT terminology.) When FC mode is in effect, instructions from a newer XSLT version than the processor supports are one class of unknown instructions. For example, the xsl:namespace instruction is a new-for-2.0 way to emit a namespace node. The 1.0 processor won't be able to execute the instruction, but there is a way to cause a namespace node to be emitted as a byproduct of creating an attribute whose name is in a previously-unused namespace.


Listing 9. Using the Fallback element
<!-- The stylesheet must be versioned 2.0 for proper FC. -->
<xsl:template name="emitNS">
  <!-- This template must be called before any child nodes 
        have been created for the element. -->
  <xsl:namespace name="my" select="'http://www.example.com/NS/data'">
    <xsl:fallback>
      <xsl:attribute name="my:ignore" 
         namespace="'http://www.example.com/NS/data'">junk</xsl:attribute>
    </xsl:fallback>
  </xsl:namespace>
</xsl:template>

Analysis: The 2.0 processor will simply execute the xsl:namespace instruction and ignore the fallback block. The 1.0 processor will operate in FC mode (due to version="2.0" on the stylesheet) and will encounter xsl:namespace and treat it as an unknown XSLT instruction, which means it would only throw an error if there were no xsl:fallback child element. Because there is an xsl:fallback child element, the 1.0 processor executes it, which means it performs all the (presumably 1.0) instructions inside. The xsl:attribute instruction emits an attribute node, which is unwanted in this case, and will emit a namespace node if the namespace used in the name has not been declared previously.

Simple example showing element-available to check whether an instruction is supported

Here is an example where the 1.0 processor might implement an extension element similar to the xsl:result-document element in 2.0 (assume that you assign the "redirect" prefix to an appropriate namespace).


Listing 10. Verifying instruction support
<xsl:template version="2.0" match="/doc/foo">
  <xsl:choose>
    <xsl:when test="element-available('xsl:result-document')">
      <xsl:result-document href="{@file}">
        <xsl:call-template name="someContent" />
      </xsl:result-document>
    </xsl:when>
    <xsl:when test="element-available('redirect:write')" 
              use-when="element-available('redirect:write')">
      <redirect:write select="@file">
        <xsl:call-template name="someContent" />
      </redirect:write>
    </xsl:when>
    <xsl:otherwise>
      <xsl:message 
         terminate="no">Warning!  Can't serialize 
           to <xsl:value-of select="@file" /></xsl:message>
    </xsl:otherwise>
  </xsl:choose>
</xsl:template>

Analysis: The 1.0 processor will run in FC mode, preventing an error on xsl:result-document and on the @use-when in the second branch. The second branch has two guards for the extension element: @test for 1.0 and @use-when for 2.0 (which the 2.0 processor will catch in an early phase, removing the branch and avoiding an unknown-instruction error). All 2.0 processors must support xsl:result-document and, therefore, will take the first branch. (The @use-when criterion for the second branch could just as well have been the processor version.)

Taking advantage of new features in xsl:output

Here is an example that allows the 2.0 processor to take of advantage of the new output method XHTML and the new character-map feature in xsl:output, while maintaining 1.0 behavior if executed by a 1.0 processor. (For more about xsl:character-map including an example, see Part 1 of this series.)


Listing 11. xsl:output offers new features
<-- main stylesheet module -->
<xsl:stylesheet version="2.0">
<xsl:import href="imp2.xsl" />

<xsl:output method="html" 
            doctype-system="http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd" 
            doctype-public="-//W3C//DTD XHTML 1.0 Strict//EN" />

<xsl:template match="/">
  <xsl:result-document format="output20">
    <xsl:fallback>
      <xsl:call-template name="main" />
    </xsl:fallback>
    <xsl:call-template name="main" />
  </xsl:result-document>
</xsl:template>

    ...

</xsl:stylesheet>

Now here is the imp2 stylesheet imported by the preceding stylesheet:


Listing 12. Version 2.0 output declarations
<-- imported stylesheet module - imp2.xsl -->
<xsl:stylesheet version="2.0">

<xsl:output method="xhtml" name="output20" use-character-maps="myMap"/>

<xsl:character-map name="myMap">
    ...
</xsl:character-map>

</xsl:stylesheet>

Analysis: When this stylesheet is processed by a 1.0 processor, it will merge all xsl:output declarations from both the main and imported stylesheet modules. In the merged xsl:output, the following attributes are defined: @method, @doctype-system, @doctype-public, @name, and @use-character-maps. Where attributes values conflict (@method defined in both stylesheets), the value from the xsl:output declaration with the higher import precedence overrides attributes from a lower import precedence. Thus, the attribute method="html" from the main module takes precedence over method="xhtml" from the imported stylesheet. Because a 1.0 processor analyzes the merged xsl:output declaration in FC mode, both the unknown-to-1.0 @name and @use-character-maps are cleanly ignored. When the first template executes, fallback occurs because xsl:result-document is not supported in 1.0. Thus, 1.0 behavior is maintained. For a 2.0 processor, an xsl:result-document instruction without an href attribute will produce an implicit final result tree, just as if the element xsl:result-document wasn't present. However, specifying it allows additional serialization control of the implicit tree by adding @format, which indicates which (named) xsl:output applies. Thus, a 2.0 processor only calls serialization using attributes defined from the imported xsl:output.


Use of islands for both 1.0 and 2.0 processors

At least one feature of XSLT 2.0 is probably driving you to upgrade. Where a 2.0 processor is deployed, it can handle the old code if it supports the BC feature and, of course, can handle the new 2.0 features. (See Part 3 of this series for a discussion of optional features for 2.0, including BC, and Part 4 for details of how BC works.) But the 1.0 processors must be placated as long as you continue to use them, which always involves isolating the 2.0 code to avoid errors, and almost always involves having alternative code for the 1.0 processor. As discussed in Part 2 of this series and in the planning grid in this article, some of the new 2.0 features are more amenable to isolating into islands than others.

You could set version 2.0 at the template level, but a 1.0 processor will throw an error when it executes an XSLT element with an unknown attribute, unless FC mode is in effect. To put the 1.0 processor in FC mode, set the version higher than 1.0 in a place where the 1.0 processor is expecting the version to be set, which is only on the xsl:stylesheet outer element or on an LRE at a higher level. So you need [xsl:]version="2.0" at a more global level than the template anyway. Similarly, local versioning won't help on individual XSLT instructions within a sequence constructor or in subsidiary elements like xsl:sort or xsl:when. (Setting version 2.0 on a template or lower-level element might occasionally be necessary to instruct a 2.0 processor to stop executing in BC mode. A better practice is to rewrite the code so that BC mode is not necessary.) By setting version 2.0 globally, you enable the 1.0 processor to ignore @use-when, which is part of a more forward-looking approach.

Making 2.0 instructions usable

You probably have 1.0 stylesheets that work reasonably well. This gives you material for the 1.0 islands. The 2.0 features you want to use will go into the 2.0 islands, but those islands might have to expand up to the point where there is a clean substitution between 1.0 and 2.0 islands that do equivalent work. For example, if you are motivated to use a certain new function from 2.0, you would have to expand its island to an XPath expression at least, if there is a similar expression for the 1.0 island. If the expressions aren't substitutable, you might have to expand up to substitutable templates or global xsl:variable elements. Listing 8 illustrates this point.

The situation for instructions that existed in 1.0 becomes more complicated when you want to use new features on those instructions. First of all, new-for-2.0 attributes will be ignored, which might prevent the instruction from doing useful work. For example, xsl:attribute takes @select in 2.0 as an alternative to having a sequence constructor, but if you use the new style, the 1.0 processor will not have anything that tells it what value the attribute should have. Conversely, xsl:value-of took @select in 1.0, while 2.0 offers the alternative of having a sequence constructor, but the 1.0 processor will probably throw an error upon encountering the child elements, regardless of FC mode. (FC mode for 1.0 is vague about unexpected child elements on known XSLT instructions, but the general expectation is that misplaced XSLT elements should cause an error. The 1.0 spec for FC does not specifically say that 1.0 elements occurring in places where they should not be in 1.0 are to be afforded immunity from errors. In other words, you can count on the 1.0 processor not recognizing the sub-element in that position, but you can't count on the processor to ignore the sub-element.)

A non-fatal example is that xsl:variable takes @as in 2.0, which asserts a particular datatype for the variable. A 1.0 processor will ignore @as and assign the type using its built-in policies, but if you wanted to force a particular datatype, you are probably depending on that type when you use the variable. Similarly, the xsl:output declaration takes new attributes in 2.0, but if you use them, you are already thinking in 2.0 terms. (See the 2.0 declarations section for more discussion of new attributes of xsl:output and xsl:variable, and also a discussion of the compatibility of the new structure of the xsl:key declaration.)

In general, separate islands of 2.0 code at either the template level or in separate imported stylesheet modules, as shown in Listings 2, 3, and 7 in this article. A few 2.0 instructions can be cleanly substituted using the fallback mechanism within a sequence constructor. Most other situations involve usage of 2.0 features in ways that can't be isolated as cleanly at the fine-grained level. For example, if you create a variable that contains a sequence of atomic values, only a 2.0 processor is able to handle such a variable or have the instructions to do useful things with it. The planning grid provides some suggestions about isolating 2.0 code.

Making 2.0 declarations usable

Declaration elements that are new for 2.0 are ignored by 1.0. (See Part 4 of this series for more explanation of declarations and other XSLT terminology.) In this article, you saw how to circumvent xsl:function in Listings 3 and 8. Part 1 of this series discussed xsl:character-map. For new serialization features that are enabled through attributes defined in xsl:output, a 1.0 processor cannot take advantage of them but you can still maintain existing 1.0 capabilities, while you also enable new 2.0 features if the processor can handle them. See Taking advantage of new features in xsl:output for one technique. The other new declaration is xsl:import-schema, which is only for schema-aware processors and generally does not have a 1.0 alternative.

XSLT 2.0 made two changes to xsl:key and one change to the associated key() function:

  • The use attribute, which was required in 1.0, becomes optional and a sequence constructor can replace it . This is the first case of xsl:key taking a child element.
  • Value comparisons can be based on datatypes other than string, and for strings, you can designate a collation to use.
  • An optional third argument to key() specifies the document and/or sub-tree to search.

The first change shouldn't disturb your legacy code. Simply put, a stylesheet that needs to be run by both 1.0 and 2.0 processors must define the key values with @use (the old way) rather than a sequence constructor. The second change might be an issue, but you can use BC mode to force all comparisons to be string comparisons. Specifically, you put version="1.0" in scope on the xsl:key declaration to force string comparisons. (You can still put @collation on there, which will be ignored by the 1.0 processor but allow the 2.0 processor to perform string comparisons using the designated collation.) The third change will be a great relief to experienced users of keys, because it saves having to wrap xsl:for-each awkwardly around the key() call. However, a 1.0 processor will not allow the key function to be called with three arguments. Consider using the technique shown in Listing 3 to have separate 1.0 and 2.0 versions of the key declarations and of the templates where key() is called.

The xsl:variable element has changed very little at the syntactic level. For 2.0, a new as attribute is available, which sets the datatype of the variable. If a 1.0 processor encountered this attribute when in FC mode, it should not raise an error and the variable is assigned a datatype under the 1.0 rules. For example, consider this declaration in a version 2.0 stylesheet:

<xsl:variable name="return-interval" as="xs:duration" select="'P5D'"/>

In this case, a 1.0 processor ignores the @as and assigns the type string to the variable. Yet a 2.0 processor assigns the duration type to the same variable. Usage of the variable other than for simple display, which always emits a string, is caught between two worlds. If you needed it to have the duration type, which would be your motivation for using @as when creating it, then you are asking for capabilities that are beyond what a 1.0 processor can do.

Most of the other declarations are exactly the same in 2.0 as in 1.0. There is a new convenience feature for the children of xsl:attribute-set, but it is reasonable to keep using the 1.0 style.


Feature-by-feature planning grid

Table 1 shows some of the ways in which you will modify particular aspects of the stylesheet.


Table 1. Planning grid to upgrade to 2.0
New 2.0 FeatureCreating the 2.0 codeCoexistence of 1.0 and 2.0

xsl:character-map - Controlling output character-by-character

In Part 1 of this series, refer to the section on "Output (Serialization) enhancements" for more information.

See Listings 11 and 12.

Date/Time processing and formatting

In Part 1 of this series, refer to the section on "Date/time data "for more information.

See Listing 6.

xsl:function - Adding stylesheet functions

In Part 1 of this series, refer to the section on "User-defined functions" for more information.

See Listing 8.

xsl:for-each-group - Arranging elements into groups

In Part 1 of this series, refer to the section on "Grouping" for more information.

If the legacy grouping code can be isolated cleanly, see Listing 2 or Listing 3.

key() - Using the new third argument

Replace existing code that had to change the context before calling the key function.

See commentary about keys.

@mode - using keywords (#current, #all, #default) or a list of modes

Use enhancements to modes if it makes your stylesheet simpler by removing duplicate templates.

Probably not a good candidate for version coexistence.

xsl:output - Using new serialization options such as:

  • BOM
  • escape-uri-attributes
  • include-content-type
  • normalization-form
  • undeclare-prefixes
  • XHTML

In Part 1 of this series, refer to the section on "Output (Serialization) enhancements" for more information.

See Listings 11 and 12.

xsl:result-document - Creating multiple result documents in one pass

Refer to the developerWorks tip Create multiple files in XSLT 2.0 for more information.

See Listing 10.

Schema types - working with XML Schema data types

In Parts 1 and 3 of this series, refer to the sections on Schema Awareness for more information.

Probably not a good candidate for version coexistence.

New string functions and xsl:analyze-string

Replace existing named templates and extension functions with the new 2.0 functions or instructions where needed.

See Listing 6 or Listing 9.

Using a sequence constructor within xsl:value-of instead of the select attribute.

This is one of two cases where an attribute that was required in 1.0 becomes optional in 2.0 (the other is in xsl:key). The main reason you might want to have a sequence constructor is when your sequence cannot be reduced to a single XPath expression.

If you want the same xsl:value-of to be used by both 1.0 and 2.0 processors, use the select attribute.


The art of version portability

To help you decide whether to take that approach, this article spells out the consequences of retaining a 1.0 processor in production use while you begin to use new 2.0 features in your stylesheets. Your users who are served through an XSLT 2.0 processor should gain better performance or appearance, though they won't realize that the stylesheets are becoming more readable and maintainable. Once all the 1.0 processors are replaced by 2.0 processors, you can rip out the old code and the guarding mechanisms that caused the 1.0 processors to select it. Ripping out old code is especially easy if it was already separated by imported modules, as shown in the recommended practice example.


Resources

Learn

Get products and technologies

  • IBM trial software: Build your next development project with trial software available for download directly from developerWorks.

Discuss

About the authors

David Marston has worked with XML technologies since late 1998, particularly on standards conformance. Over his 25+ years in the computing business, he has been involved with all aspects of software development. He is a graduate of Dartmouth College and a member of the ACM. He is on the Next-Generation Web team at IBM Research. You can contact him at David_Marston@us.ibm.com.

Joanne Tong is a developer working on IBM's XSLT processors in the IBM Toronto lab. She is currently an editor of the W3C XSLT 2.0 and XQuery 1.0 Serialization specification and is an active member of the XSL working group. You can contact her at joannet@ca.ibm.com.

Report abuse help

Report abuse

Thank you. This entry has been flagged for moderator attention.


Report abuse help

Report abuse

Report abuse submission failed. Please try again later.


developerWorks: Sign in

If you don't have an IBM ID and password, register here.


Forgot your IBM ID?


Forgot your password?
Change your password


By clicking Submit, you agree to the developerWorks terms of use.

 


The first time you sign into developerWorks, a profile is created for you. This profile includes the first name, last name, and display name you identified when you registered with developerWorks. Select information in your developerWorks profile is displayed to the public, but you may edit the information at any time. Your first name, last name (unless you choose to hide them), and display name will accompany the content that you post.

Choose your display name

The first time you sign in to developerWorks, a profile is created for you, so you need to choose a display name. Your display name accompanies the content you post on developerWorks.

Please choose a display name between 3-31 characters. Your display name must be unique in the developerWorks community and should not be your email address for privacy reasons.

(Must be between 3 – 31 characters.)


By clicking Submit, you agree to the developerWorks terms of use.

 


Rate this article

Comments

Help: Update or add to My dW interests

What's this?

This little timesaver lets you update your My developerWorks profile with just one click! The general subject of this content (AIX and UNIX, Information Management, Lotus, Rational, Tivoli, WebSphere, Java, Linux, Open source, SOA and Web services, Web development, or XML) will be added to the interests section of your profile, if it's not there already. You only need to be logged in to My developerWorks.

And what's the point of adding your interests to your profile? That's how you find other users with the same interests as yours, and see what they're reading and contributing to the community. Your interests also help us recommend relevant developerWorks content to you.

View your My developerWorks profile

Return from help

Help: Remove from My dW interests

What's this?

Removing this interest does not alter your profile, but rather removes this piece of content from a list of all content for which you've indicated interest. In a future enhancement to My developerWorks, you'll be able to see a record of that content.

View your My developerWorks profile

Return from help

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=1
Zone=XML
ArticleID=210056
ArticleTitle=Planning to upgrade XSLT 1.0 to 2.0, Part 5: Make your stylesheets work with any processor version
publish-date=02272007
author1-email=David_Marston@us.ibm.com
author1-email-cc=dwxed@us.ibm.com
author2-email=joannet@ca.ibm.com
author2-email-cc=dwxed@us.ibm.com

Tags

Help
Use the search field to find all types of content in My developerWorks with that tag.

Use the slider bar to see more or fewer tags.

For articles in technology zones (such as Java technology, Linux, Open source, XML), Popular tags shows the top tags for all technology zones. For articles in product zones (such as Info Mgmt, Rational, WebSphere), Popular tags shows the top tags for just that product zone.

For articles in technology zones (such as Java technology, Linux, Open source, XML), My tags shows your tags for all technology zones. For articles in product zones (such as Info Mgmt, Rational, WebSphere), My tags shows your tags for just that product zone.

Use the search field to find all types of content in My developerWorks with that tag. Popular tags shows the top tags for this particular content zone (for example, Java technology, Linux, WebSphere). My tags shows your tags for this particular content zone (for example, Java technology, Linux, WebSphere).