Advanced XSL

Up to now, we have examined the basics of XSLT. In fact, we could get along very nicely using just what we have learned so far. However, there are a number of situations that the XSLT basics do not cover. We will look at some of the more advanced features of XSL in this section. These features are not advanced in terms of complexity—in fact, some are quite simple—but advanced in that they move beyond what a simple stylesheet might do.

Sequential and Non-Sequential Processing

Up to now, we have processed all XML documents in a sequential fashion—in original document order. However, it is possible to process documents non-sequentially, multiple times and conditionally. We shall see examples of these kinds of processing in the following sections.


Modes

Modes are a way by which the same elements within an XML document can be processed more than once, each time with a different result. Listing 5.21 shows an XSL stylesheet that processes County elements twice. Lines 7 and 8 cause each of the template rules (lines 10 and 14) to be processed.

Code Listing 5.21. mode.xsl—Processing Elements Multiple Times
 1: <?xml version="1.0"?>
 2: <xsl:stylesheet
 3:     xmlns:xsl="http://www.w3.org/XSL/Transform/1.0"
 4:     indent-result="no" default-space="strip">
 5: <xsl:template match="text()"/>
 6: <xsl:template match="/">
 7:     <xsl:apply-templates match="County" mode="name"/>
						8:     <xsl:apply-templates match="County" mode="all"/>
 9: </xsl:template>
10: <xsl:template match="County" mode="name">
11:     CountyName:<xsl:value-of select="@Name"/>
12: </xsl:template>
13:
14: <xsl:template match="County" mode="all">
15:     <xsl:for-each select="Town">
16:         TownName:    <xsl:value-of select="TownName"/><xsl:text>&#xD;&#xA;</xsl:text>
17:     </xsl:for-each>
18: </xsl:template>
19: </xsl:stylesheet>
20:

The rules for using the mode= attribute are simple:

  • If an xsl:template element does not have a match= attribute, it cannot have a mode= attribute.

  • For xsl:apply-template elements, if the rule has a mode= attribute, it is only applied to xsl:template rules with a matching mode= attribute.

Conditional and Repeat Processing

We've seen many ways to process XML documents, most of which are sequential in nature. Basically, we find some element that matches an xsl:template rule and then apply the rule. However, sometimes we know more about the structure of the original document and want to apply other processing to its elements. We've already encountered xsl:for-each in several examples but never discussed it specifically. xsl:for-each allows us to apply the same processing to each child element of a selected element in their original document order. In Listing 5.11, we wanted to show every listing price, and xsl:for-each worked nicely.

But suppose we wanted to see each listing and categorize each as in our price range, slightly above our price range, or completely out of our price range? We can handle the first case simply with xsl:if. The following XSL snippet labels real estate listing either within our price range or not and can be found on the CD-ROM as xsl-if.xsl.

<xsl:template match="Listing">
   <xsl:if test="ListingPrice &lt;= 150000" >
       In our range:<xsl:value-of select="."/>
    </xsl:if>
   <xsl:if test="ListingPrice > 150000" >
      Out of range:<xsl:value-of select="."/>
   </xsl:if>

xsl:if is simple to use and has a single attribute test= where any XSL expression can be evaluated. If the expression evaluates to true, the rule is applied; otherwise, it's not. We could output only even-numbered elements by writing

<xsl:template match="Listing">
   <xsl:if test="position() mod 2 = 0" >
       Evens<xsl:value-of select="position()"/>):<xsl:value-of select="."/>
    </xsl:if>
   </xsl:if>

We can perform much more sophisticated processing via xsl:choose. Anyone familiar with a C, C++, or Java switch statement will be comfortable with xsl:choose. The format of xsl:choose statements is as follows:

<xsl:choose>
    <xsl:when test='some test to perform'>
        <!-- Some processing -->
    </xsl:when>
    <xsl:when test='another test to perform'>
        <!-- Other processing -->
    </xsl:when>
    <xsl:otherwise>
        <! Default processing when no xsl:when is applicable>
    </xsl:otherwise>
</xsl:choose>

Each xsl:choose element has one or more xsl:when children and optionally an xsl:otherwise child. The test described for each xsl:when is performed sequentially in the order listed and, if all tests fail, the xsl:otherwise element is processed. In reality, xsl:choose is nothing more than a standard switch statement in XSL clothing.

Sorting

Sorting and numbering are two areas we have only briefly discussed. We've seen the position() built-in function repeatedly but have not used it in anything more than a trivial way. Let's look at sorting first. We can put position, or any expression really, to use for the purpose of ordering output. For example, suppose rather than determining whether listings are within a price range we'd rather sort all listings in ascending order. Listing 5.22 does something like that, using xsl:sort.

Code Listing 5.22. sort.xsl—Sort Listings by price
 1: <?xml version="1.0"?>
 2: <xsl:stylesheet
 3:     xmlns:xsl="http://www.w3.org/XSL/Transform/1.0"
 4:     indent-result="no" default-space="strip">
 5: <xsl:template match="text()"/>
 6:
 7: <xsl:template match="//Town[TownName='Shirley']">
 8:     <xsl:apply-templates select="Listing">
 9:         <xsl:sort select="numberListingPrice"/>
10:     </xsl:apply-templates>
11: </xsl:template>
12:
13: <xsl:template match="Listing">
14:     <xsl:value-of select="."/>
15: </xsl:template>
16: </xsl:stylesheet>

The reason I said "something like that" rather than "exactly that" or something similar is because we don't get exactly what is expected; our prices are treated like strings. This brings us to the exact usage of xsl:sort. xsl:sort has four attributes in addition to the select= attribute. Each of these optional attributes adds additional control over sort order. They are

  • order='ascending|descending'—Defines the order of the sort—default is ascending.

  • data-type='text|number'—Defines how the data should be interpreted—default is text.

  • lang='somelang'—Defines the language in which the data should be interpreted. Uses the same set of values as xml:lang. For example, lang='fr' for French, or lang= 'en-US' for U.S. English—default is determined by the system environment.

  • case-order='upper-first|lower-first'—Default case order is language specific.

Numbering

Numbering is the natural companion to sorting. Suppose we wanted to number the Listing elements from the prior section as well as sorting them. We could have added a single line to Listing 5.21, line 13a, and caused our elements to be numbered neatly on output.

13:<xsl:template match="Listing">
13a:    <xsl:number value="position()"/>
14:     <xsl:value-of select="."/>
15: </xsl:template>

xsl:number also has several additional attributes other than the value= attribute that control the format of the resulting output number:

  • level='single|multiple|any'—Specifies what levels of the tree should be considered. Default is single. single counts all siblings at the current level +1 (for itself) that match the count= pattern. Thus for our two sets of Listing elements (one within each Town), each list would be counted separately.

  • multiple counts all ancestors of the current node +1 (for itself) that match the count= pattern. any counts nodes that match at any level within the document.

  • from='starting'—Specifies the starting value to count from. For example, from='10' would starting numbering at 10.

  • count='somepattern'—The count= attribute specifies a pattern that the selected node must match to be counted.

  • format='a format'—The format= attribute specifies how the output should look and is a subset of the type attribute of the HTML 4.0 element OL. Example formats might be

    '1.' For 1., 2., 3., and so on

    'a' for a, b, c, and so on

    'A' for A, B, C, and so on

    'i' for i, ii, iii, iv, v, and so on

Number Formatting

In general, number formatting is defined by section 7.7.1 of the XSLT specification, Number to String Conversion Attributes. See this section for a complete list of all the attributes for formatting numbers.


Listing 5.23 shows a much more complete listing of numbering that makes use of the count= attribute to count County, Town, and Listing entries, each of which has its own template rule that uses <xsl:number...> with a format to number the individual levels. Listing 5.24 shows an abbreviated result after running XT using this stylesheet and our original input REListing.xml

Code Listing 5.23. sort-2.xsl—Sort and Number at Multiple Levels
<?xml version="1.0"?>
<xsl:stylesheet
    xmlns:xsl="http://www.w3.org/XSL/Transform/1.0"
    indent-result="no" default-space="strip">
<xsl:template match="text()"/>
<xsl:template match="/REListing">
   <xsl:number level="multiple" count="County|Town|Listing" />
    <xsl:apply-templates/>
</xsl:template>

<xsl:template match="County">
   <xsl:number format="I"/>
    <xsl:text> </xsl:text>
    <xsl:value-of select="@Name"/>
    <xsl:text>&#xD;&#xA;</xsl:text>
    <xsl:apply-templates/>
</xsl:template>
<xsl:template match="Town">
   <xsl:number format="i"/>
    <xsl:text> </xsl:text>
    <xsl:value-of select="TownName"/>
    <xsl:text>&#xD;&#xA;</xsl:text>
    <xsl:apply-templates/>
</xsl:template>

<xsl:template match="Listing">
   <xsl:number format="1."/>
    <xsl:text> </xsl:text>
    <xsl:value-of select="."/>
</xsl:template>
</xsl:stylesheet>

Code Listing 5.24. Result of Applying sort-2.xsl to REListing.xml
I Worchester
   i Shirley
        1.
            The Real Estate Exchange
            Colonial
            189000
            7 Something Place
            1999
            3
            1.5
            40,000
            A Small Three Bedroom Colonial set on 1 acre
. . .
        6.
            Gosselin Grp.
            Land
            129900
            15 Birchwood road
            Last lot in pristine neighborhood
            1.5
            Will not last!
    ii Lunenburg
        1.
            R.E. LLP
            Mansion
            1200000
            1 Lakeside Ave
            1952
            10
            5
            2
            Absolutely incredible mansion right on the lake!
. . .
        5.
            Gosselin Grp.
            Land
            529900
            4 Lakeshore Drive
            Deep water dock rights!
            2.5
            One of the last lots on the lake!

Variables

Before we move on to creating content, there is one other area of XSL that we need to explore: xsl:variable. XSL allows us to define variables, which are just content bound to a name, using the xsl:variable element. In its simplest form, variables are simply placeholders for other contents. For example, we could define a simple variable as follows:

<xsl:variable name='author'>Al Saganich</xsl:variable>

We could then reference this variable via xsl:value-of. For example:

<xsl:value-of select="$author"/>

In fact, there are two ways to define variables, although the second is not supported by XT at the time of this writing.

<xsl:variable name="SomeName">some value</xsl:variable>

and

<xsl:variable name="SomeName" select="some value"/>

We can use variables in expressions. For example, we could have created a variable that defined the position we wanted to select from a set of listings or perhaps the maximum price we could afford for a home. Let's re-examine selecting inexpensive homes using variables as shown in Listing 5.25.

Code Listing 5.25. Cheap-3.xsl—Using Variables
<?xml version="1.0"?>
<xsl:stylesheet
    xmlns:xsl="http://www.w3.org/XSL/Transform/1.0"
    indent-result="no" default-space="strip">
<xsl:variable name='maximum'>150000</xsl:variable>
<xsl:template match="text()"/>
<xsl:template match="Listing[ListingPrice &lt; $maximum]">
    Cheap!:<xsl:value-of select="."/>
</xsl:template>
</xsl:stylesheet>

A few notes on variables:

  • xsl:param is another way to say xsl:variable.

  • Variables can reference other variables.

    The current version of XT does not support this and does not expand the underlying variables.

  • We insert the value of a variable into an expression by prefixing the variable name with a dollar sign ($).

  • Variable values cannot be changed on-the-fly and are similar to variables defined with Java's final keyword.

  • Two variables cannot be of the same name and the same scope. For example

    <xsl:template ...>
        <xsl:variable name='a'>some value</xsl:variable>
        <xsl:param name='a'>some other value</xsl:param>
    </xsl:template>
    

    is illegal, whereas

    <xsl:template ...>
        <xsl:variable name='a'>some value</xsl:variable>
    </xsl:template>
    <xsl:template ...>
        <xsl:param name='a'>some other value</xsl:param>
    </xsl:template>
    

    is perfectly legal.

Creating Content

There are a number of other ways to insert content directly into an output document. We some times want to insert unchanged text (xsl:text), add an attribute (xsl:attribute) or entire elements (xsl:element), or entire lists of elements (xsl:copy). Whatever the case may be, XSL has a construct for it

xsl:text

We've already seen xsl:text several times. It allows us to insert information directly into our output document as well as allowing us to disable output escaping. For example:

<xsl:text disable-output-escaping="no">&lt;</xsl:text>

results in &lt; being output. Whereas

<xsl:text disable-output-escaping="yes">&lt;</xsl:text>

results in < being output.

Outputing Scripting Elements

Earlier versions of the XSL Specification had a real problem with outputting JavaScript and the like because most scripting languages use less than as a character and don't recognize the HTML 4.0 entity set. With xsl:text and disabling output escaping, we can output pretty much whatever we want without worrying that the XSL translation engine will muck it up.


xsl:attribute

We can add attributes directly into elements via xsl:attribute. For example, suppose we had a separate .html file for each town in our original example? We could generate HTML links for each by writing the following:

<xsl:template match="//Town">
    <a>
    <xsl:attribute name="href">
        <xsl:value-of select="TownName"/>.html</xsl:attribute>
    The wonderful town of <xsl:value-of select="TownName"/>
    </a>
</xsl:template>

Simple and straightforward enough!

A few important notes on adding attributes:

  • The attribute must be written within an element. For example, the <a>...</a>.

  • Attributes must be written before any child elements.

  • Attributes must not contain non-textual data except for [cr] and [nl], which can be inserted exactly as given or via their appropriate entities.

xsl:element

Rather than a single attribute, we sometimes want to create an entire element, and with xsl:element we can. If we look back to our original REListing.xml file, we can see that Countys have Name and Description as attributes rather than as child elements. We could recreate the County element with Name and Description as child elements via listing 5.26

Code Listing 5.26. Element.xsl—Recreate County Elements with Children
<?xml version="1.0"?>
<xsl:stylesheet
    xmlns:xsl="http://www.w3.org/XSL/Transform/1.0"
    indent-result="no" default-space="strip">
<xsl:template match="text()"/>
<xsl:template match="//County">
   <xsl:element name="County2">
							      <name><xsl:value-of select="@Name"/></name>
							      <desc><xsl:value-of select="@Desc"/></desc>
							   </xsl:element>
</xsl:template>
</xsl:stylesheet>

In fact, we could have added comments and attributes to the result as well.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.140.185.170