Chapter 4

Templates Rule!

In This Chapter

bullet Introducing template rules

bullet Finding out how a template is processed

bullet Using xsl:apply-templates, xsl:copy, xsl:copy-of, and xsl:value-of

bullet Introducing built-in template rules

bullet Matching the best template

bullet Working with attribute value templates

bullet Understanding named templates

T emplate rules are the foundation of XSLT. Not only do they specify what information should be pulled from the source document, but template rules also define what the resulting document is going to look like. Think of them as the bricks of a chimney, the shingles of a roof, the pages of this book, nuggets in a 12-piece McNugget Value Meal. Well, you get the idea, right? Template rules are the basic building blocks of XSLT and nothing much happens in “transformation-land” without them. To use current slang, templates rule!

A Glorified Mail Merge

Template rules can be thought of as a glorified mail merge. Have you ever used the mail merge feature in a word processor? Even if you haven’t, the concept is pretty simple. To do a mail merge that creates a personalized letter for each customer in a database, for example, you create a letter in a word processor that includes both normal text and data field placeholders. These fields in the word processed document are linked to an external customer database, so that when you print the document, the word processor prints a letter for each customer in the linked database. This mail merge process is very common; in fact, I am willing to bet you have mail sitting in your mailbox right now that was prepared in this manner.

The customer database serves as the source of the information. The word processor document serves as the template. And a printed letter results from the transformation in which the source and template are combined.

Similarly, an XML document acts as the source, while the XSLT stylesheet contains templates. The XSLT processor creates a result document based on the source XML document being applied to the stylesheet’s template rules. Table 4-1 shows the parallels between the two processes.

Table 4-1 Comparing Mail Merge with XSLT Template Rules
Item Mail Merge XSLT
Data Source External database XML document
Template Word processor document XSLT stylesheet with
with linked fields template rules
Result Printed letter with merged Result document, such as
information a web page

Some may argue that it is an oversimplification to compare XSLT transformations with a mail merge, but even if it is, the comparison isn’t far off and is certainly a good starting point as you get your hands around template rules.

Basics of a Template Rule

A template rule transforms a set of XML nodes you specify from the source document into a new look. A “cookie cutter” template inside the template rule contains all the specifications for what this new look should be like. You create a temple rule using an xsl:template element with basic boilerplate code that looks like this:

<xsl:template match=””>

</xsl:template> 

As shown in Figure 4-1, each template rule consists of two key parts: the match pattern and the template.

Figure 4-1: Parts of a template rule.

Figure 4-1: Parts of a template rule.

Pulling with match patterns

The match pattern is an XPath expression that pulls the nodes that you want to transform from the source tree. Think of a match pattern as something like the list your parents gave you to go to the supermarket for eggs, milk, and cheese. When you entered the store, you would scurry up and down all of the aisles searching for the specific groceries on the list. When you found an item, you’d drop it into the basket and continue until everything was checked off of the list. (And if you were like me, you probably threw in a candy bar for good measure in the checkout aisle.)

In the same way, a match pattern defines a list of nodes that you want to be included in the result document (also known as a result tree). It does so by specifying the conditions you want a node to meet in order for it to be included. The template rule uses the match pattern and scurries through the source document looking for nodes that match these conditions. (However, I’ve yet to see a template rule throw in a candy bar into the result document!)

To be used by the template rule, the match pattern is placed as the value of the match attribute and takes the form of a specific XPath expression called a location path.

Tip

XML source and result documents are treated as trees by XSLT because they have a hierarchical tree-like structure to them. Therefore, for most purposes, the terms source document and source tree are interchangeable, as are result document and result tree. For more information on trees, see Chapter 3.

You can find out about XPath location paths in Chapter 5, but I can explain a bit now to help you get the gist of what they do for template rules. Consider the XML file in Listing 4-1.

Listing 4-1: score.xml

<!-- score.xml -->

<scores>

  <score id=”1”>

    <film>A Little Princess</film>

    <composer>Patrick Doyle</composer>

    <year>1995</year>

    <grade>100</grade>

  </score>

  <score id=”2”>

    <film>Chocolat</film>

    <composer>Rachel Portman</composer>

    <year>2001</year>

    <grade>90</grade>

  </score>

  <score id=”3”>

    <film>Vertigo</film>

    <composer>Bernard Herrmann</composer>

    <year>1956</year>

    <grade>95</grade>

  </score>

  <score id=”4”>

    <film>Field of Dreams</film>

    <composer>James Horner</composer>

    <year>1989</year>

    <grade>96</grade>

  </score>

  <score id=”5”>

    <film>Dead Again</film>

    <composer>Patrick Doyle</composer>

    <year>1991</year>

    <grade>97</grade>

  </score>

</scores>

From this source document, suppose you want to get each of the film elements and do something with them. To do so, set your match element to be the name of the element:

<xsl:template match=”film”>

  <!-- Do something with the film elements --> 

</xsl:template>

In plain English, this match pattern says:

“Hey Mr. XSLT Processor, as you examine each of the nodes in the source tree, look for a child of the current node that is named film. If you find a match, please return the node to the template rule so that I can do something with it.”

Each of the five film elements is processed by the template rule.

Remember

XPath location paths can get pretty . . . uh . . . shall I say “interesting.” (Translation: They can look like gobbledygook!) Don’t concern yourself too much with XPath location paths now. I save all that fun for Chapter 5.

Pushing with templates

Because of their similarity, the terms template and template rule are often confused and tend to be sloppily interchanged. But there is a fundamental difference in meaning: A template rule is the entire xsl:template element, and the template is everything inside the start and end tags of xsl:template. The purpose of the template is to define how the returning node set is pushed (or output) to the result tree.

A template contains two types of information:

bullet Literal text: Plain text that is simply copied to the result tree.

bullet XSLT instructions: XSLT instructions that generate text or nodes for the result tree. Common elements you use include xsl:apply-templates, xsl:copy, xsl:copy-of, and xsl:value-of.

In the following example, The film: is normal text, and the xsl:value-of instruction in an XSLT element that is evaluated at processing time to generate text:

<xsl:template match=”score”>

  The film: <xsl:value-of select=”film”/>

</xsl:template>

How a template rule is processed

When a template rule is processed, the XSLT processor reads through an incoming XML document and assembles it as a tree. After this tree structure is defined, the processor starts walking through each node of the tree, looking for the most specific matching template rule for each node it encounters. When it finds a match, the processor uses the selected template as its guideline for how to add the node set to the result tree.

To illustrate, suppose you have the XML snippet shown in Listing 4-2.

Listing 4-2: tv.xml

<tv>

   <model>1010</model>

   <type>WideScreen</type>

   <aspectratio>16x9</aspectratio>

</tv> 

And want to output it :

Model 1010 has an aspect ratio of 16x9. 

The template rule that can do this transformation looks like:

<xsl:template match=”tv”> 

  Model <xsl:value-of select=”model”/> has an aspect ratio of <xsl:value-of select=”aspectratio”/>.

</xsl:template>

When the XSLT processor performs this transformation, it first constructs a source tree like the one shown in Figure 4-2. Next, as it gets to each node, it checks to see if the node matches the lone template rule I’ve defined. The processor scores a bull’s-eye when it gets to the root node, because the root node has a tv element node as its child.

Figure 4-2: Source tree.

Figure 4-2: Source tree.

The processor then gets the template rule’s template and combines the literal text with the results of the two xsl:value-of elements (see Figure 4-3). These are combined into a single text node and added to the result tree’s root node, as shown in Figure 4-4.

Figure 4-3: Text nodes of the template.

Figure 4-3: Text nodes of the template.

Figure 4-4: Result tree.

Figure 4-4: Result tree.
Tip

When working with namespaces in your stylesheets, a good rule is that elements in a stylesheet in the xsl: namespace are part of the XSLT language, while non-xsl: elements within a template rule are literal elements put into the result tree.

Common Action Instructions

Within a template rule’s template, a few key XSLT instructions do the yeoman’s work of generating nodes for the result documents. These action instructions you use most often are xsl:apply-templates, xsl:copy, xsl:copy-of, and xsl:value-of.

Tip

In XSLTSpeak, top-level XSLT elements, such as xsl:template or xsl:output are called elements, whereas XSLT elements in the template rule that are used to create part of the result tree are called instructions.

xsl:apply-templates

The xsl:apply-templates element is perhaps the most commonly used instruction inside template rules. Its most basic syntax is:

<xsl:apply-templates  select=”expression” />

The select attribute (shown in italic) is purely optional.

Tip

Think of xsl:apply-templates as something like a stereotypical factory supervisor. It doesn’t really do anything by itself, but is charged with telling others, “Hey you, do this.”

The purpose of xsl:apply-templates is to invoke the template rule most appropriate for the current node and its children. It doesn’t really do anything by itself, but tells another template rule to do something. In some cases, the template rule called upon is the one that contains the xsl:apply-templates instruction. In other cases, it may be another template rule defined in your stylesheet. And, if nothing else is found, the XSLT processor applies one of XSLT’s built-in template rules. (I discuss built-in template rules later in the chapter.)

Two obvious questions follow: How does xsl:apply-templates know which nodes to process? And after a node or node set is selected for processing, how does xsl:apply-templates decide which template rule to apply? I discuss the answers to these questions in the next two sections.

Scope of xsl:apply-templates

The xsl:apply-templates instruction processes nodes within its context or scope. When its select attribute is not defined, then its scope is the node set returned from the match pattern of the template rule it is inside of. For example, in the code snippet below, xsl:apply-templates processes the tv element and its children from the XML file shown in Listing 4-2:

<xsl:template match=”tv”>

  <xsl:apply-templates>

</xsl:template> 

For each node in the node set, its template rule is applied along with all of its children’s template rules.

Or, if select is defined, only the nodes that result from the expression are transformed, along with their children. For example, when the following template rule is processed on the tv.xml file in Listing 4-2, only the aspectratio element is applied by xsl:apply-templates, the tv, model, and type elements are ignored by this xsl:apply-templates instruction:

<xsl:template match=”tv”>

  <xsl:apply-templates select=”aspectratio”>

</xsl:template> 

Looking for the best template rule

For each node that is processed by xsl:apply-templates, the XSLT processor looks for the best template rule to apply to it.

If the instruction’s select attribute is defined, the processor will look for the most appropriate template rule for that node. But if select isn’t defined, the template rule that contains xsl:apply-templates is chosen for the current node.

However, in both of these cases, don’t forget about the children. The current node’s children are also processed, so the XSLT processing engine goes through the same process of looking for the most appropriate template rule for each child. If a template rule is found matching that node, it will then be processed. But if not, then a built-in template rule is processed instead.

Here’s a summary of the behavior of xsl:apply-templates:

bullet Looks for the most appropriate template rule to apply for each node in the node set. If no template rule has been defined, xsl:apply-templates will apply a built-in template rule.

bullet With no select attribute, processes all children.

Remember

In addition to elements, xsl:apply-templates also applies text nodes that are children of the current node.

Consider a few examples to get a better grasp of its intended actions. I use the following XML fragment in Listing 4-3 as the source document in each of these.

Listing 4-3: miniscore.xml

<!-- miniscore.xml -->

<score id=”1”>

  <film>A Little Princess</film>

  <composer>Patrick Doyle</composer>

  <year>1995</year>

  <grade>100</grade>

</score>

Using xsl:apply-templates on the score element, the template rule is defined as:

  <xsl:template match=”score”>

    <xsl:apply-templates/>

  </xsl:template>

This template rule processes the score node and its children (using built-in templates, which I discuss later in the chapter) to send their content (in other words, text nodes) to the result tree. Because the score element has no text content, no text is added to the result tree. But, because the score element has four child elements that have text nodes, the child elements’ content is transferred to the result document:

  A Little Princess

  Patrick Doyle

  1995

  100

Using xsl:apply-templates with a select attribute, the template rule supplied is defined as follows:

  <xsl:template match=”score”>

    <xsl:apply-templates select=”grade”/> is the critic’s rating for the musical score of <xsl:apply-templates select=”film”/>

  </xsl:template>

In this template rule, I apply the template specifically on the grade and film elements to add their content to the result tree. In between these two, I add literal text to the output. The result is:

100 is the critic’s rating for the musical score of A Little Princess

The other children of the score element (composer and year) were not added to the result tree, because the xsl:apply-templates’s select attribute didn’t match these elements.

If you use xsl:apply-templates on an element with no children, only that particular element is processed:

  <xsl:template match=”film”>

    <movie><xsl:apply-templates/></movie>

  </xsl:template>

The preceding template sends the following text to the result tree:

<movie>A Little Princess</movie>

xsl:copy

You can use the xsl:copy element to do just what you’d expect with a name like that — this element copies the current node. It has a basic syntax of:

<xsl:copy></xsl:copy>

However, keep in mind some specific behaviors to this xsl:copy instruction:

bullet Preserves the current node’s start and end tags during processing.

bullet Doesn’t copy children.

bullet Doesn’t copy attributes.

bullet Includes content text only if you put an xsl:apply-templates inside the xsl:copy element. An empty xsl:copy (<xsl:copy/>) doesn’t copy content.

bullet Has no select attribute, so it acts only on the result of the template rule’s match pattern.

To illustrate, I use the miniscore.xml file (refer to Listing 4-3) as my source document. Using an empty xsl:copy element when the composer element is the match pattern, the code is defined as:

  <xsl:template match=”composer”>

    <xsl:copy/>

  </xsl:template>

The literal result of this template is <composer></composer>, but the XSLT processor shortens the empty tag to:

<composer/>

As you can see, the primary purpose of xsl:copy is to carry over the element tags. However, if you combine it with xsl:apply-templates, you copy both the tags and its content:

  <xsl:template match=”composer”>

    <xsl:copy>

      <xsl:apply-templates/>

    </xsl:copy>

  </xsl:template>

This template outputs:

<composer>Patrick Doyle</composer>

When you use xsl:copy and xsl:apply-templates on a current node that has children, the template rule copies the current node’s tags and the content of the children. For example:

  <xsl:template match=”score”>

    <xsl:copy>

      <xsl:apply-templates/>

    </xsl:copy>

  </xsl:template>

Results in:

<score>

  A Little Princess

  Patrick Doyle

  1995

  100

</score>

xsl:copy-of

xsl:copy-of may be similar in name to xsl:copy, but its behavior is quite distinct. It has a basic syntax of:

<xsl:copy-of select=”expression”/>

While xsl:copy provides a carbon copy of some parts of the returned node set, xsl:copy-of does more — it duplicates everything inside the current node. Specifically, xsl:copy-of:

bullet Copies all the nodes returned from its required select attribute.

bullet Preserves all element tags during processing.

bullet Copies children, attributes, and content.

bullet Keeps any comments or other processing instructions in the copy.

To copy the score element from the miniscore.xml (refer to Listing 4-3) to a new result document, I set up a new template rule. The template rule for copying the score element as is from the source tree to the result tree is defined as:

  <xsl:template match=”score”>

    <xsl:copy-of select=”.”/>

  </xsl:template>

The template rule’s match pattern returns a score element, and the . expression inside the select attribute of the xsl:copy-to returns the current node (and all its children), producing the following output:

<score id=”1”>

  <film>A Little Princess</film>

  <composer>Patrick Doyle</composer>

  <year>1995</year>

  <grade>100</grade>

</score>

The select attribute of the xsl:copy-of element determines what is copied to the result tree. Suppose you define a more limited template rule:

  <xsl:template match=”score”>

    <xsl:copy-of select=”year”/>

  </xsl:template>

This template rule results in a literal copy of only the year element (both tags and content):

<year>1995</year>

xsl:value-of

The xsl:value-of instruction is used when you want to convert part of the XML source to a string (plain text). It has a basic syntax of:

<xsl:value-of select=”expression”/>

This instruction wants nothing to do with producing XML tags, so you never get elements or attributes when you use it. Specifically, the xsl:value-of element has the following behavior:

bullet Converts the result from its required select attribute to a string and adds it as a text node to the result tree. If the result is a single node, its contents are converted. If the result is a node set, the contents of the first node are used for processing.

bullet Removes element tags during processing.

bullet Doesn’t convert attributes of the context node.

bullet Is combined with other text nodes that surround it before being added to the result tree.

Using the miniscore.xml file in Listing 4-3 as the source, if I use xsl:value-of on a single element (film), the template rule looks like:

  <xsl:template match=”score”>

    <xsl:value-of select=”film”/>

  </xsl:template>

The film element is converted to text:

A Little Princess

xsl:value-of can also be used to convert an attribute to a string. For example, if I use a special @ character in the select attribute to denote an attribute:

  <xsl:template match=”score”>

    <xsl:value-of select=”@id”/>

  </xsl:template>

It returns the following output:

1

Table 4-2 summarizes the behavior of xsl:copy, xsl:copy-of, and xsl:value-of.

Table 4-2

Remember

A common mistake many XSLT stylesheet authors make is using a match attribute in place of the select attribute in an xsl:copy-of, xsl:apply-templates, or xsl:value-of instruction. You’ll get a processing error if this occurs in your stylesheet.

Built-In Template Rules

As you begin to work with template rules, you need to know some behind-the-scenes activities I informally call “lazy man’s template rules” because you don’t have do anything in order to have them run. These actions are actually called built-in template rules, and the XSLT processor uses them to process any node that isn’t matched with an explicitly defined template rule in your stylesheet. Different built-in template rules are applied to each node type.

Remember

The template rules you create in your stylesheet override built-in template rules.

Element nodes

For element nodes, I demonstrate the built-in template rule by using the ever-popular miniscore.xml file (refer to Listing 4-3) as the source document. Suppose you transform it by using an empty stylesheet:

<xsl:stylesheet xmlns:xsl=”http://www.w3.org/1999/XSL/Transform” version=”1.0”> 

</xsl:stylesheet> 

The output is:

<?xml version=”1.0” encoding=”utf-8”?>

  A Little Princess

  Patrick Doyle

  1995

  100

Now, this output looks eerily familiar to the first example using xsl:apply-templates. That is no accident, because the built-in template that is run on element nodes and the root node looks like the following:

<xsl:template match=”*|/”>

  <xsl:apply-templates/>

</xsl:template>

The match pattern is a global catchall to process all nodes (child nodes of the current node and the root node), and xsl:apply-templates transforms each element and sheds the tags.

Text and attribute nodes

For text and attribute nodes, a built-in template rule copies their text through into the result tree:

<xsl:template match=”text()|@*”>

  <xsl:value-of select=”.”/>

</xsl:template>

In this rule, the match pattern returns all text nodes with text() and all attribute nodes with @*. The xsl:value-of instruction then transforms the current node to the result tree as text.

Tip

If you wanted to strip out all text nodes, use the following template rule to override the built-in template rule for text nodes:

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

Processing instructions, comments, and namespaces

A built-in template rule strips processing instructions and comments so that neither is carried over to the result tree:

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

The match pattern returns every processing instruction and comment nodes; because the template is empty, nothing is added to the result tree.

Namespace nodes are also removed during processing. An XPath expression for matching a namespace node doesn’t exist, so you can’t express namespaces as a built-in template or override this built-in rule with your own.

Tip

You also have the option of not defining a template for a template rule. If you did so, the template rule looks like this:

<xsl:template match=”film”></xsl:template>

or

<xsl:template match=”film”/>

When this template rule is triggered, there is no template to output, so it outputs nothing.

When a template rule doesn’t contain a template, the rule removes the match pattern from the output.

Matchmaker, Make Me a Match

So far in this chapter, I have been using examples that have had only a single template rule within the stylesheet. You can combine template rules in the same stylesheet, which is actually standard practice. However, suppose the processor evaluates a node in the source tree and finds that two or more of the template rules match. Which of the matches wins?

XSLT prioritizes template rules based on a fairly complex system of weighting, but Table 4-3 shows the more common cases (where Level 1 is highest priority, Level 4 is lowest):

Table 4-3 Priority of Common Template Rules
Level Description of Rule Example
1 (most specific) Element name with a filter <xsl:template match=
”score[@id=’1’]”>
1 Element as part of a path <xsl:template match=
”scores/score”>
2 Element name <xsl:template match=
”score”>
3 Node test <xsl:template match=
”node()”> or <xsl:
template match=”*”>
4 Built-in template rule <xsl:template 
match=”*|/”>
TechnicalStuff

You can override the XSLT’s built-in prioritization scheme by explicitly declaring your own priority for a template rule. You do this by using the priority attribute.

To illustrate, suppose I use the score.xml file (refer to Listing 4-1) as the source document and then want to apply the following XSLT stylesheet:

<xsl:stylesheet xmlns:xsl=”http://www.w3.org/1999/XSL/Transform” version=”1.0”> 

  <xsl:output method=”xml” omit-xml-declaration=”yes”/> 

  <xsl:template match=”score”>

   Almost: <xsl:apply-templates select=”film”/> 

  </xsl:template>

  <xsl:template match=”score[@id=’1’]”>

   The Best: <xsl:apply-templates select=”film”/> 

  </xsl:template>

  

</xsl:stylesheet>

The first template rule returns a match when the current node has a score element node as a child. The second template rule returns a match when the current node has a score element node with an id attribute that equals 1.

So, when the source tree is evaluated, the XSLT processor finds two template rules that match when the current node is found to have a score element node with an id attribute equaling 1. However, the second template rule, being more specific to this particular node, is considered higher in priority; therefore, the node is matched to the second template rule, not the first. The result document looks like the following text output:

   The Best: A Little Princess

  

   Almost: Chocolat

  

   Almost: Vertigo

  

   Almost: Field of Dreams

  

   Almost: Dead Again

Working with Attribute Value Templates

When your result document is XML or HTML format, you often have an occasion to plug in the result of an XPath expression as the value of an attribute. Designed specifically for this purpose, an attribute value template is located inside a template and surrounded by curly braces ({}).

For example, suppose I use the following XML snippet as the source:

<score>

  <film>A Little Princess</film>

  <composer>Patrick Doyle</composer>

  <year>1995</year>

  <grade>100</grade>

</score>

If I convert each of the score’s child elements to attributes of the score element, the end result looks like this:

<score year=”1995” grade=”100” composer=”Patrick Doyle” film=”A Little Princess”/>

To perform this transformation, I start by writing a new element as literal text in my template rule, putting empty quotes as the values:

<score year=”” grade=”” composer=”” film=””/>

Using attribute value templates, I then enclose the name of each child node in curly braces. The curly braces tell the XSLT processor to evaluate what’s inside each of them as an expression:

  <xsl:template match=”score”>

    <score year=”{year}” grade=”{grade}” composer=”{composer}” film=”{film}”/>

    <xsl:apply-templates/>

  </xsl:template>

  <xsl:template match=”film”/>

  <xsl:template match=”composer”/>  

  <xsl:template match=”year”/>

  <xsl:template match=”grade”/>

As you can see, I also defined empty template rules for the child elements to remove them from the result tree. If I don’t explicitly define these empty template rules, they are added as attributes (through the attribute value templates) and their text nodes (through xsl:apply-templates) are also added.

Working with Named Templates

A named template is an xsl:template element that has a name attribute but no defined match attribute. Normally, you declare a match attribute for a template rule so that when the processor encounters the rule, it processes the rule immediately based on this match pattern. In contrast, a named template doesn’t contain a match attribute, so another template or instruction using xsl:call-template must explicitly call a named template.

To demonstrate, I use the miniscore.xml (refer to Listing 4-3) as the source. Suppose I want to append a text string to several of the template rules I apply to the source document. Rather than adding the same text string to each of the template rules, I can instead create a single named template that is called by each of the template rules and have the named template actually do the real work. Consider the following XSLT stylesheet:

<xsl:stylesheet xmlns:xsl=”http://www.w3.org/1999/XSL/Transform” version=”1.0”> 

  <xsl:template name=”CreditLine”>

    <xsl:value-of select=”.”/> - Brought to you by Tumplates, The Template People.

  </xsl:template>

  <xsl:template match=”film”>

    Film: <xsl:call-template name=”CreditLine”/>

  </xsl:template>  

  <xsl:template match=”composer”>

    Composer: <xsl:call-template name=”CreditLine”/>

  </xsl:template>  

  <xsl:template match=”year”/>

  <xsl:template match=”grade”/>

 

</xsl:stylesheet>

The first xsl:template element is the named template. When called, it adds text to the result document using xsl:value-of. However, without a match attribute defined, the XSLT processor has nothing to evaluate so the processor ignores the named template unless it’s explicitly called somewhere else.

The next two template rules then use xsl:call-template to call the CreditLine named template, which executes based on the node set returned from the calling template’s match pattern. The output then looks like:

  Film: A Little Princess - Brought to you by Tumplates, The Template People.

  

  Composer: Patrick Doyle - Brought to you by Tumplates, The Template People.

Tip

To find out how to use named templates in combination with parameters, see Chapter 8.

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

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