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.
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.
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
useattribute, which was required in 1.0, becomes optional and a sequence constructor can replace it . This is the first case ofxsl:keytaking 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 Feature | Creating the 2.0 code | Coexistence of 1.0 and 2.0 |
|---|---|---|
| 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. |
| In Part 1 of this series, refer to the section on "User-defined functions" for more information. | See Listing 8. |
| 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. |
| Replace existing code that had to change the context before calling the | |
| Use enhancements to modes if it makes your stylesheet simpler by removing duplicate templates. | Probably not a good candidate for version coexistence. |
| In Part 1 of this series, refer to the section on "Output (Serialization) enhancements" for more information. | See Listings 11 and 12. |
| 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 | Replace existing named templates and extension functions with the new 2.0 functions or instructions where needed. | |
Using a sequence constructor within | This is one of two cases where an attribute that was required in 1.0 becomes optional in 2.0 (the other is
in | If you want the same |
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.
Learn
-
The W3C site is a great source of information on standards such as:
- XSLT 1.0
- XSLT 2.0
- XSLT 2.0 Requirements
- XPath
- XPath 2.0
- Functions and Operators (F&O)
- Data Model (XDM)
- Formal Semantics
- Serialization
- XQuery
- XML Schema
- XML 1.1
- XHTML
- Namespaces in XML
- "Improvements in XSLT" (David Marston and Joanne Tong, developerWorks, October 2006): In the first part of this series, discover which XSLT 2.0 features are likely to motivate an upgrade. You'll also find some material about XPath 2.0 features.
- "Five strategies for changing from XSLT 1.0 to 2.0" (David Marston and Joanne Tong, developerWorks, November 2006): Read the second part of this series, which describes the higher-level decision factors for planning an upgrade to XSLT 2.0, setting the stage for using Backwards and Forwards Compatibility as transition tools.
- "Why the transition from 1.0 to 2.0 needs planning" (David Marston and Joanne Tong, developerWorks, November 2006): In the third part of this series, peruse a buyer's guide to XSLT 2.0 processors.
- "The Toolkit for XSLT portability" (David Marston and Joanne Tong, , developerWorks, February 2007): In Part 4 of this series, look at the complete toolkit for mixing code of different versions.
- IBM XML certification: Find out how you can become an IBM-Certified Developer in XML and related technologies.
- XML technical library: See the developerWorks XML Zone for a wide range of technical articles and tips, tutorials, standards, and IBM Redbooks.
- developerWorks technical events and webcasts: Stay current with technology in these sessions.
- The EXSLT site: Review a set of extension functions for XSLT 1.0.
- Create multiple files in XSLT 2.0" (Jack Herrington, developerWorks, March 2005): Find examples of
xsl:result-documentin action in this developerWorks tip.
Get products and technologies
- IBM trial software: Build your next development project with trial software available for download directly from developerWorks.
Discuss
- XML zone discussion forums: Participate in any of several XML-centered forums.
- developerWorks blogs: Get involved in the developerWorks community.
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.
Comments (Undergoing maintenance)





