Login | Register   
LinkedIn
Google+
Twitter
RSS Feed
Download our iPhone app
TODAY'S HEADLINES  |   ARTICLE ARCHIVE  |   FORUMS  |   TIP BANK
Browse DevX
Sign up for e-mail newsletters from DevX


advertisement
 

Transitioning from XSLT 1.0 to 2.0, Part 2

Lean which factors make XSLT 2 a technology worth exploring for your own development needs.


advertisement
The recursive nature of XSLT1 template calls provided a great deal of power, but at the cost of often requiring that stateful information that a particular template might need be passed in from an ancestor template through each intermediate template. A consequence of this was that deep templates might end up having to pass a great deal of information via parameters that were otherwise not used at all within any of the intermediate templates, adding considerably to the verbosity of XSLT scripts and increasing the probability that a single misplaced template parameter could end up resulting in many wasted hours trying to figure out why the recipient parameter was empty.

This example of template flows highlights a number of limitations that the original XSLT 1.0 transformations had, and these, unfortunately, generally couldn't easily be handled via adding new XPath functionality, because it was fundamentally a problem with XSLT itself. For this reason, one of the more intriguing ideas that emerged was the concept of tunneling. In a tunnel, parameters are added to a template, with the attribute @tunnel="yes". In a different template, an <xsl:call-template> or <xsl:apply-templates> is called with <xsl:with-parameter> having the same name and similarly sporting a @tunnel="yes" attribute. For instance,

<!-- template #1 --> <xsl:template match="body">     <body>     <xsl:apply-templates select="*">           <xsl:with-param name="username" select="string(@username)" tunnel="yes"/>     </xsl:apply-templates>     </body> </xsl:template> <!-- template #2 --> <xsl:template match="p">     <p><xsl:apply-templates select="*|text()"/></p> </xsl:template> <!-- template #3 --> <xsl:template match="*|text()">     <xsl:copy><xsl:apply-templates select="@*"/><xsl:apply-templates select="*|@text()"/></xsl:copy> </xsl:template> <!-- template #4 --> <xsl:template match="span[@class='username']">     <xsl:param name="username" tunnel="yes"/>     <span class="username"><xsl:value-of select="$username"/></span> </xsl:template>

will convert the XML fragment:


<body username="Kurt Cagle">     <h1>User</h1>     <p>The username is <span class="username"/></p> </body>

into

<body>     <h1>User</h1>     <p>The username is <span class="username">Kurt Cagle</span></p> </body>

The tunnel was called with parameters in template #1, passed transparently through templates #2 and #3, then retrieved the parameter for template #4 in order to populate the username . It's worth noting here that the tunneling is still in force -- if a descendent of template #4 sets up tunneling for the username, that template could still retrieve the value.

Another extraordinarily useful new feature with XSLT2 is the @use-when attribute. This attribute, when placed on any XSL element, will perform the Boolean test given within the attribute (in the current context of the element) and will then invoked the action of that element if the value is true. For instance, if you set up a debug flag at the top of the stylesheet, then any element with an associated @use-when will only be run if the debug flag is set to true:

<xsl:variable name="debug" select="'true()'"/> <xsl:template match="foo">     <xsl:for-each select="*" use-when="$debug=true()">            <div class="debug"><xsl:value-of select="name(.)"/></div>     </xsl:for-each>     <div class="foo"><xsl:apply-templates select="*"/></div> </xsl:template>

If the $debug flag is true(), this will generate a list of the names of each of the child elements for the <foo> element, then will create a wrapper and apply-templates for all the children. If $debug is false(), only the wrapper is displayed.

The @use-when attribute can also apply to individual elements, but, the attribute has to incorporate the xsl: namespace prefix. For instance,

<div xsl:use-when="$debug = true()">This is a debug message</div>

will only be displayed if the $debug flag is properly set.

For templates, this is the logical successor to the @mode attribute, which made it possible to use the same pattern in different contexts but that suffered from being static. For instance, there was no clean way with @mode to create a "switch" like capability, with @use-when there is. What's more, the @use-when attribute makes it much easier to decompose template match statements, since you could effectively create two or more templates that would handle different but similar actions in small blocks rather than having to create huge, conditional statements within an XSLT2 stylesheet.

The <xsl:apply-imports> makes it possible to works in a manner similar to but it works exclusively on imported stylesheets, even if they have the same match signature. With XSLT2, this also includes the ability to pass parameters to the imported templates, making them far more functional in nature. Apply-imports can consequently build in an XML-esque version of class inheritance into the XSLT space.

The <xsl:analyze-string> element provides additional regular expression support by accepting a selection node-set and a @regex attribute containing a regular expression, then can use the <xsl:matching-substring> and <xsl:non-matching-substring> children in order to create template content appropriate to the expression. For instance, with the XML structure:

<aaa>     <bbb>How do you do?</bbb> </aaa>

You can use the structure to split it cleanly into distinct parts.

<xsl:stylesheet                  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"                  version="2.0">            <xsl:output  method="xml"                        indent="yes"                        omit-xml-declaration="yes"/>            <xsl:template  match="/aaa">                  <xsl:analyze-string  select="bbb"                              regex="\S+">                        <xsl:matching-substring>                              <ccc>                                    <xsl:value-of  select="."/>                              </ccc>                        </xsl:matching-substring>                  </xsl:analyze-string>            </xsl:template>      </xsl:stylesheet>

This generates the output:

<ccc>How</ccc>      <ccc>do</ccc>      <ccc>you</ccc>      <ccc>do?</ccc>

Note that you could do this with tokenize() as well, but it would take more code to write.

There are more capabilities that XSLT 2.0 offers (Ginzu knives and all!), but most of these tend to be either expansion of existing function sets (such as a whole range of date functions), optimization of template flow and organization or similar "tweaks" that could still knock off a few percent from processing time or number of lines of code necessary to perform transformations.

XSLT1 vs. XSLT2

While the advantages offered by XSLT 2.0 are sizeable compared to 1.0, it is still worth examining the costs of making such a transition. At this stage, arguably the best XSLT 2.0 engine is that produced by Saxonica, now in it's ninth iteration. The Saxon XSLT Processor has been improved continuously for the last dozen years, and exists now in both Java and .NET flavours, and because it's primary developer, Michael Kay, is also the editor of the XSLT 2.0 specification (and the upcoming 2.1 specification), Saxon has also become both the testbed and reference implementation for the W3C.



Comment and Contribute

 

 

 

 

 


(Maximum characters: 1200). You have 1200 characters left.

 

 

Sitemap
Thanks for your registration, follow us on our social networks to keep up-to-date