XSLT Tips


By now, you have been introduced to the XSLT basics and even some of the more advanced topics. One of the challenges in working with XSLT, however, is deciding the best route to take when you want to solve a problem. In this section, I provide some tips to help you solve some common XSLT problems.

Generating content in specialized ways

When content is generated, a generic content stream is generated. Using the xsl:output, specific modifiers can be added and removed to generate correctly formatted data. But with XSLT, it is also possible to generate specific content like comments. To generate a comment, the following XSLT can be used:

    <xsl:template match=”*”>         <xsl:comment>My Comment</xsl:comment>        </xsl:template>

When the XSLT is executed, the following content is generated:

<?xml version="1.0" encoding="UTF-8"?><!--My Comment--> 

Notice how the xsl:comment instruction has been converted to <!-- -->. For XML this is a correct way of specifying a comment. But when generating generic text it might not be the correct way. You manage the generic generation process by using the xsl:output instruction. The advantage of using the xsl:comment instruction is that it is generic, and the xsl:output generates the correct comment.

It is also possible to generate processing instructions using the following sample XSLT document:

    <xsl:template match=”*”>         <xsl:processing-instruction name=”special”>action=”do action”</xsl:processing-instruction>     </xsl:template>

When the XSLT is executed, the following content is generated:

<?xml version=”1.0” encoding=”UTF-8”?><?special action=”do action”?>

Parsing keys in attributes

Consider the following XML:

    <child colors=”red green blue”>         <elements index=”1”>Hello</elements>     </child>

The problem is that the attribute colors has three keys that need to be split apart. Some XSLT processors have specific tokenize functions that can split apart the list into the individual data sets. But the task is to solve this problem using standard XSLT. In a traditional programming approach, the developer uses a loop and then finds each token. This is not easy to do in XSLT because looping requires a child node. The actual solution is shown in the following XSLT document:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">     <xsl:template name="ExtractColors">         <xsl:param name="allColors" />         <xsl:choose>             <xsl:when test="contains( $allColors, ‘ ‘) ">                 Found(                 <xsl:value-of select="substring-before( $allColors, ‘ ‘)" />                 )                 <xsl:call-template name="ExtractColors">                     <xsl:with-param name="allColors"                           select="substring-after(     $allColors, ‘ ‘)"/>                  </xsl:call-template>             </xsl:when>             <xsl:when test="string-length( $allColors) > 0">                 Found(                 <xsl:value-of select="$allColors" />                 )                 <xsl:call-template name="ExtractColors">                     <xsl:with-param name="allColors"                            select="substring-after(     $allColors, ‘ ‘)"/>                  </xsl:call-template>             </xsl:when>         </xsl:choose>     </xsl:template>     <xsl:template match="child">         <xsl:variable name="colors">             <xsl:call-template name="ExtractColors">                 <xsl:with-param name="allColors" select="@colors"/>              </xsl:call-template>         </xsl:variable>         Found Colors {{<xsl:value-of select="$colors" />}}         <element>             <xsl:value-of select="text()" />         </element>         <xsl:apply-templates />     </xsl:template>     <xsl:template match="/">         <data>             <xsl:apply-templates />         </data>     </xsl:template>     <xsl:template match="text()" mode="?">     </xsl:template> </xsl:stylesheet>

Looping on something other than node-sets is best handled using a recursive template call. To split apart the colors, the best approach is to generate an XML stream of nodes that are then assigned to a variable. In the XSLT document, the xsl:template match child has a child xsl:variable that calls the template ExtractColors. The template ExtractColors then makes use of the substring-before and substring-after functions to split apart the string. The recursion takes place when a substring-before function finds something and recursively calls itself, but the data passed in is substring-after.

Remember that looping can often be solved by using recursion, especially when doing string tokenization. Another way of doing looping is by using something called the Piez method, but it relies on tricking the XSLT processor. Although this technique works, I do not recommend it because it makes debugging and figuring out problems more difficult. This means maintenance and extensions could be difficult to program.

Counting attribute items

Instead of splitting the individual attribute items into a number of tokens, you can count how many tokens there are without actually performing a tokenization. Consider, again, the XML code introduced in the preceding section:

    <child colors=”red green blue”>         <elements index=”1”>Hello</elements>     </child>

The problem now is to figure out how many colors are available. The solution is provided in the following XSLT document:

<xsl:template match=”child”>     Count(     <xsl:value-of select=        “string-length(@colors) - string-length(translate(@colors,’ ‘, ‘’)) + 1”/>     ) </xsl:template>

This a sneaky solution to the problem. What happens is that the string length before and after the translate function is calculated. The translate function removes all character sequences specified when calling the translate function, which in the example are spaces. The number of spaces plus one equals the number of tokens in the buffer. The reason this works is because a space or other defined separator are required to identify each token. Although this works generally, there are some things to watch for. In the sample XML, the spaces were evenly located one space apart. If there were extra spaces, the count would be incorrect. The way to solve this is to call the function normalize-string to remove those spaces. If you use other tokens, such as commas or colons, you won’t have the problem of an incorrect count.

Generating child nodes

Consider the following XML:

<data>     <text>         Here I am <b>working</b>at a job     </text> </data> 

In this XML, there is an XML node and some text, with a node embedded within. Instead of embedding the text, you should make the nodes siblings, similar to the following:

<data>     <text>         Here I am </text><b>working</b><text>at a job     </text> </data>

The solution to this problem is the following XSLT:

<xsl:stylesheet version=”1.0” xmlns:xsl=”http://www.w3.org/1999/XSL/Transform”>     <xsl:template match=”data/text”>         <xsl:apply-templates />     </xsl:template>     <xsl:template match=”data/text/text()”>         <text><xsl:value-of select=”.”/></text>     </xsl:template>     <xsl:template match=”data/text/b”>         <b><xsl:value-of select=”.”/></b>     </xsl:template>     <xsl:template match=”/”>         <data><xsl:apply-templates /></data>     </xsl:template> </xsl:stylesheet>

The trick that this XSLT uses is that it matches on specific text nodes using the data/text/text() XPath. This splits the parsing process into multiple chunks. Notice that the xsl:template match on every text() XPath was removed. This was done because, otherwise, the XSLT does not work properly. However, as a twist, the original xsl:template with a match on every node could have been left in if a condition were added. That condition tests the parent node and then generates the proper block.

Accessing large amounts of data quickly

Consider the following XML document:

<data>     <row>something</row>     <row>more</row>     ...     <row>Finally there</row> </data> 

This node-set is huge, and the problem is to find an element quickly without having to constantly iterate through the entire list. The solution to this problem is to set up an xsl:key instruction similar to the following:

<xsl:key name=”find-fast” match=”row” use=”.” />

Next, you reference an individual element by using the key function. Note that this approach is also very useful if you need to constantly reference a specific list. For example, you iterate a node-set in which an attribute references an item from the keyed set. The advantage of the key function is that it is a simple one-line solution.

Finding the parent path

Consider the following XML document:

<data>     <text>         Here I am <b>working</b>at a job     </text> </data>

At each node, you want to find the parent path and index. When you know the parent path and index, they can be used as metadata information or information for later processing. The XSLT solution is as follows:

<xsl:stylesheet version=”1.0” xmlns:xsl=”http://www.w3.org/1999/XSL/Transform”>     <xsl:template match=”*”>          <xsl:for-each select=”ancestor::*”>             /<xsl:value-of select=”name()”/>[<xsl:number/>]         </xsl:for-each>         <xsl:apply-templates />     </xsl:template>     <xsl:template match=”/”>         <xsl:apply-templates />     </xsl:template>          </xsl:stylesheet>

The solution to this XSLT is that a repetition of parent selection is executed by using the XPath ancestor::*. The position of the node is found using the xsl:number instruction.

Getting more help

There are always situations when the information provided in a book isn’t quite enough, and you want to ask somebody who knows. Your question most likely has already been answered, and here are two places where plenty of XSLT information is stored:

  • The World Wide Web Consortium (www.w3.org): Offers an XSLT mailing list. You can access the mailing list archives at www.biglist.com/lists/ xsl-list/archives/.

  • The XSLT FAQ (www.dpawson.co.uk/xsl/xslfaq.html): Provides the answers to many of the most frequently asked questions about XSLT.




The XMLSPY Handbook
The Official XMLSPY Handbook
ISBN: 764549642
EAN: 2147483647
Year: 2001
Pages: 121
Authors: Larry Kim

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net