Pattern Matching with match= and select=

We've seen some basic XSL as well as several tools for processing and using XSL. What we need now is a more in-depth understanding of how the match= and select= attributes to xsl:template and xsl:apply-templates elements function.

Let's start from the top and examine the whole pattern matching process more closely. Each document contains a root element. The root element is selected via the match="/" attribute to xsl:template. The select= attribute of the xsl:value-of element works almost identically. Both match= and select= work on patterns. Examples of patterns might be

  • match="/"—Match the root element.

  • select="Listing"—Select any Listing element child.

  • match="*"—Match any element.

  • match="Town|County"—Match any Town element or any County element.

  • match="@attribute"—Match any source element with the named attribute.

In addition to the match= attribute of xsl:template and the select= attribute of xsl:apply-templates, patterns are also used in numbering and keys. The rules in this section apply equally well to both of these areas.

Of course many other possibilities exist. In fact the XSLT specification contains a complete set of production rules for specifying patterns. However, they are a little dry and are perhaps better explained via example. As we shall see, match= and select= can contain rather sophisticated expressions for matching and selecting elements.

Source Element

Each time a template rule is applied, a source element is selected. That is to say that as each element that matches the specified match criteria is processed, the selected element is termed the source element. If we had matched on a Listing element, which has children ListingBroker, Type, Addr, Price, and so on, the Listing element would be considered the source element. The select= attribute of xsl:value-of would then act on the children of Listing.


The following sections detail the various pattern rules.

Matching the Root

Under most circumstances, unless we specify output as <xsl:output method="text"/>, XSL output is well formed. In practice, it makes good sense to list rules in a stylesheet from most granular to least granular. Prudence then suggests that we start at the root of our document and we do this with the following rule (did you ever wonder whom this person Prudence is and why she is always suggesting things?):

<xsl:template match="/">
. . .
</xsl:template>

If we were translating an XML document to the HTML namespace, we would most likely write this rule as follows:

<xsl:template match="/">
<html>
<xsl:apply-templates/>
</html>
</xsl:template>

which would wrap the result document in traditional <html> </html> tags. We could then further define that processing should only continue on Listing elements via the select= attribute to xsl:apply-templates:

<xsl:template match="/">
<h2>
<xsl:apply-templates select="Listing"/>
</h2>
</xsl:template>

In fact, we could create a simple table of our listings by applying the stylesheet specified in Listing 5.3, which, when displayed, would look something similar to Figure 5.3. This stylesheet works from most granular to least, matching and processing elements down the hierarchy of our XML document.

Figure 5.3. A table of real estate listings.


Code Listing 5.3. RETable.xsl—A Stylesheet for Producing a Table of Listings
<?xml version="1.0"?>
<xsl:stylesheet
    xmlns:xsl="http://www.w3.org/XSL/Transform/1.0"
    xmlns="http://www.w3.org/TR/REC-html40" result="ns">


<xsl:template match="/">
    <html><body>
    <xsl:apply-templates/>
    </body></html>
</xsl:template>

<xsl:template match="Header">
    <head><title><xsl:value-of select="."/></title></head>
    <center><h1><xsl:value-of select="."/></h1></center>
</xsl:template>
<!--This rule causes the State tag to be ignored-->
<xsl:template match="State"></xsl:template>
<xsl:template match="County">
    <h2>Within the county of:<xsl:value-of select="@Name"/></h2>
    <xsl:value-of select="@Description"/>
    <xsl:apply-templates match="Town"/>
</xsl:template>

<xsl:template match="Town">
    <h3><xsl:value-of select="TownName"/> </h3>
    <table>
    <th>Listing Broker</th>
    <th>Property Type</th>
    <th>Price</th>
    <th>Address</th>
    <xsl:apply-templates match="Listing"/>
    </table>
</xsl:template>

<xsl:template match="Listing">
    <tr>
    <td><xsl:value-of select="ListingBroker"/></td>
    <td><xsl:value-of select="Type"/></td>
    <td><xsl:text>$</xsl:text><xsl:value-of select="ListingPrice"/></td>
    <td><xsl:value-of select="Addr"/></td>
    </tr>
</xsl:template>

<!-- If there isn't a template rule ignore it -->
<xsl:template/>
</xsl:stylesheet>

Literal Result Elements

There is one special case of the <xsl:template match="/"> element rule called the literal result element. This special case is designed for use when there is only a single rule within a stylesheet. Such a stylesheet is shown next. Note the differences from the average stylesheet where the root match template rule is implied:

<html xmlns:xsl="http://www.w3.org/XSL/Transform/1.0"
      xmlns="http://www.w3.org/TR/REC-html40" >
<head>
    <title>Real Estate Listing</title>
</head>
<body>
    <p><xsl:value-of select-"."/></p>
</body>
</html>

Which produces a result identical to the one produced by

<?xml version="1.0"?>
<xsl:stylesheet
    xmlns:xsl="http://www.w3.org/XSL/Transform/1.0"
    xmlns="http://www.w3.org/TR/REC-html40" result="ns">

<xsl:template match="/">
    <head>
        <title>Real Estate Listing</title>
    </head>
    <body>
        <p><xsl:value-of select-"."/></p>
    </body>
</xsl:template>
</xsl:stylesheet>

Matching Specific Elements

The second most likely case in matching patterns is matching a particular element within an XML document. Listing 5.3 showed several examples of this. In general, we simply specify the exact name of the element we want to match. Note that, as with XML in general, spelling counts. It's not an error to create a rule to match listings, even though there are none and what we most likely meant was Listing. The general rule is the same as before and is shown next. We simply replace someelement with the exact element name we are interested in.

<xsl:template match="someelement">
. . .
</xsl:template>

For example, we saw we could match Listing elements with

<xsl:template match="Listing">
<!-- Listing processing here -->
</xsl:template>

Matching Attributes

Attributes are one of the simplest patterns within XSL. We can match any attribute by simply specifying it with the at symbol (@). The template rule outputs the value of the Name attribute within County.

<xsl:template match="County">
    <xsl:value-of select="@Name"
</xsl:template>

In addition, we can combine attribute matching with the asterisk wildcard (*) and match any attribute. The following rule shows an example of this:

<xsl:template match="County">
    <xsl:for-each select="@*">
        <xsl:value-of select="."/><xsl:text>&#xD;&#xA;</xsl:text>
    </xsl:for-each>
</xsl:template>

Using xsl:value-of Review

While we haven't examined it specifically, we've used xsl:value-of repeatedly in past examples. xsl:value-of allows you to specify what value to output into the result document based on the value of the source document and on how we specify the select= attribute. We've seen the most common usage, select=".", which outputs the entire contents of the selected element as well as all its children. We've also seen how to output the value of a specific child node using select="ChildElement". And, more recently, we've seen how we can output the value of an attribute using select="@SomeAttribute". In fact, there are much more sophisticated methods to specify parent-child relationships or, more generally, ancestor-descendant relationships.

Patterns for Ancestor-Descendant Relations

So far, we have shown some of the more common methods for specifying match and select criteria and examined xsl:value-of and xsl:template as well as xsl:apply-templates in some detail. We've seen how to select one of the children of an element. However, sometimes we might want to specify ancestor-descendant relationships skipping intermediate generations, perhaps to handle arbitrary hierarchical relations between elements. We can do this by specifying these relationships separating various generations using the slash ("/").

Remembering back to our real estate listings, REListing contained Countys which contained Towns which, in turn, contained Listings which had a number of leaf elements representing individual listings. If we wanted to output only the listing broker, we could have written a rule something like that shown in Listing 5.4.

Code Listing 5.4. ListingBroker.xsl—Output Listing Brokers
<?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="/">
    <xsl:apply-templates select="REListing"/>
</xsl:template>

<xsl:template match="REListing">
    <xsl:apply-templates/>
</xsl:template>

<xsl:template match="County/Town/Listing">
    Broker:<xsl:value-of select="ListingBroker"/>
</xsl:template>
</xsl:stylesheet>

In fact, we could specify the previous rule more simply by stating

<xsl:template match="County//Listing">
    Broker:<xsl:value-of select="ListingBroker"/>
</xsl:template>

which says, give me any ListingBroker within any Listing element that's descended from County, regardless of the level at which it appears. The following rules show exactly how we tunnel down into an XML document hierarchy.

The following code matches the root of the XML Document, which, in our REListing.xml example, has two child elements—REListing and the xsl:stylesheet processing instruction.

<xsl:template match="/">
<!--Other rules -->
</xsl:template>

The following code matches the root element, which is not the same as the root, of the XML Document.

<xsl:template match="/*">
<!--Other rules -->
</xsl:template>

The following code matches the named element:

<xsl:template match="SomeElement">
<!--Other rules -->
</xsl:template>

The following matches the child of parent.

<xsl:template match="Parent/Child">
<!--Other rules -->
</xsl:template>

For example

<xsl:template match="Town/Listing">
   <xsl:value-of select="."/>
</xsl:template>

The select="." applies to the Listing element, that is the child element!

The following matches any elements with Parent, any single intermediate level, and then GrandChild elements.

<xsl:template match="Parent/*/GrandChild">
<!--Other rules -->
</xsl:template>

The following matches any elements that have a descendant somewhere beneath ancestor in the document hierarchy.

<xsl:template match="ancestor//descendant">
<!--Other rules -->
</xsl:template>

Patterns Using Built-ins

There are a number of built-in functions that can be used to match or process various elements within an XML Document.

Matching with text()

As we've seen before in Chapter 4, every leaf element has a text element child that contains the actual text associated with the element. XSL provides a built-in function for matching on and selecting text—text(). In fact, we've already seen this built-in at work in the default processing rule for nodes, shown here:

<xsl:template match="text()|@*")>
    <xsl:value-of select="."/>
</xsl:template>

Most likely, we might modify this rule to override the default processing of text nodes to discard the contents of an element rather then display it by respecifying the default rule as follows:

<xsl:template match="text()"/>

This causes text nodes not handled by another processing rule to be ignored.

Matching on ids

We can match only on elements that have a specified id by using the template rule given next. Note that this rule only matches an element with the given id. For example, if we had specified that listings each have a required id, perhaps for quick lookup, we could use that id. That is, assuming every listing had a required ID attribute, ListingNumber, we might write the following:

<xsl:template match="id('ListingID1')">
    <xsl:value-of select="."/>
</xsl:template>

to output the value of the given id.

Matching on Processing Instructions

In a similar fashion, we could output the value of a processing instruction associated with the root (again, not the root element) of an XML document as follows. Notice how we combined the "/" with processing-instruction to match PIs at the root of the document only

<xsl:template match="/processing-instruction()">
    <xsl:value-of select="."/>
</xsl:template>

Selecting Comments

While most often ignored because they imply knowledge of an XML document outside that which is provided by the DTD, we could output the value of a comment. We saw previously that comments are ignored via one of the default rules, specifically

<xsl:template match="processing-instruction()|comment()"/>

We might have wanted to output our comments and could have easily done so with the following rule:

<xsl:template match="comment()">
    <xsl:value-of select="."/>
</xsl:template>

There are a number of other built-in functions in XML stylesheets, and we will examine the remainder more closely when we look at some of the advanced features of XSL. But before we do, we need to examine one more aspect of pattern matching and selection—namely expressions.

Matching with Predicates ([])

Most of what we've done up to this point has been matching against simple location paths. Predicates can be simply thought of as a way to index into an array of nodes. Predicates can be used in other ways as well—for example:

Matches on position:

  • <xsl:template match="Listing[1]">...

    matches the first Listing

  • <xsl:template match="Listing[last()]">...

    matches the last Listing

Matches on attributes:

  • <xsl:template match="County[@Name | @Desc="Some description"]">...

    matches any County element that contains a Name attribute or has a description element containing the given text

Matches on children and descendants:

  • <xsl:template match="County[Town | Listing]">...

    Matches any County that has either Town or Listing children.

See the Xpatch specification for a complete description of the XMPath expression syntax.

In general, predicates can be expressed by the following grammar (snipped from the XPath specification):

[8]                 Predicate ::=   '['PredicateExpr ']'
[9]                 PredicateExpr::=   Expr

Expressions are further defined as:

[14]                 Expr     ::=  OrExpr
[15]                 PrimaryExpr ::=   VariableReference
                                 | '('Expr ')'
                                 | Literal
                                 | Number
                                 | FunctionCall

And so on, recursively defining expressions of greater and greater complexity. Basically, what we see is that predicates are just expressions and can be variables, other expressions using parentheses for specify precedence, literals (such as Listing and County), numbers, calls to the built-in functions, and other expressions. Let's look at expressions in more detail.

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

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