Yesterday you learned how to select element attributes from Extensible Markup Language (XML) source with XPath expressions. By using these expressions when matching templates, you achieved more control over the output from a stylesheet applied to an XML source. XPath expression templates form the cornerstone of XSLT. So, now it’s time to learn more about templates and what you can do with them.
In today’s lesson covering option templates, you will learn the following:
• Why templates are so significant in XSLT
• How to work with match templates
• How to work with named templates
• What happens when more than one template matches
• How to give priorities to templates
Templates are a key concept in XSLT. If you don’t fully understand them, you cannot fully understand and utilize the power of XSLT. To make sure that you know what templates are all about and how to use them, today’s lesson is all about templates.
You have already performed some tasks using templates, so by now you should understand what a template is and does. The next step is to build on the practical basics you know to get a deeper understanding of the template’s role.
Before you move on to new topics, you need to go back to the basics described already and formalize your understanding.
A template contains a set of instructions that are executed when the template is called. These instructions are other XSLT elements. A template either can be called explicitly or can be matched based on a rule against a node in the XML document tree being processed. Explicit calls will be discussed later in this lesson.
A template can be called based on a matching node in the XML document tree if it contains a match
attribute. The match
attribute’s value should be an XPath expression that describes which nodes in the XML document tree the template applies to.
The fact that nodes are matched rather than selected is an important difference between XSLT and programming languages such as C, Java, and Visual Basic. When nodes are matched, they are processed as the processor encounters them in the XML document tree. This means that if two nodes matched by the same template are not in sequence, they are not processed in sequence. Any nodes in between, possibly matched by another template, are processed in between. This sequence of execution is different from most programming languages, in which you specify on which data you want to operate and then that data is processed in sequence, regardless of its position within the data store it came from. Figure 4.1 helps to illustrate this operation.
The document (and hence the document tree) in Figure 4.1 is simple. Basically, this document contains two nodes named A, with one node named B in between. Now look at Listing 4.1.
ANALYSIS
Listing 4.1 is a partial listing of a stylesheet containing two templates: one matching nodes named A on line 1 and the other matching nodes named B on line 5. Because of the XML document’s structure, the output of these templates is as shown in Listing 4.2.
OUTPUT
ANALYSIS
Listing 4.2 is only a partial output (the stylesheet in Listing 4.1 is also only partially shown), but you can see that the first template is matched, then the second, and then the first again. The sequence these templates appear in the stylesheet doesn’t matter; the output is the same. Because XSLT is data driven, the data determines which template is executed. So, the templates are executed in the order the data appears in the XML source document, not the order in which the templates appear in the stylesheet.
Elements within a template are executed in the order in which they appear in the template. The elements in the template form a structure in which the data is inserted in key places. That’s why templates are called templates; that’s what they are. Templates are there to format or reformat data, so data can be displayed in an eye-pleasing manner or transformed into another data format.
In a simplistic view, XSLT is more or less like Cascading Stylesheets (CSS). Any HTML element for which a CSS style is defined is displayed in that style—for instance, bold red text in the Verdana typeface. XSLT, however, goes much further than just applying styling information; it defines a structure in which the data is inserted. In this sense, XSLT can be better compared to a mold. In a mold, you pour a liquid that solidifies in the shape of the mold; in an XSLT template, you put data that is outputted in the shape defined by the template. The essence of the two is the same: You put in something that has no predetermined shape and can therefore be shaped into anything the material is capable of, and you get out something that is shaped the way you want it. After it is shaped, getting back the original shapeable form is probably very hard. If you transform XML into HTML, the meaning of the data gets lost in the process and is replaced by formatting data. Getting back data with even a similar meaning is very tricky, if not impossible.
XSLT is all about displaying or transforming data. If you didn’t have templates, the code for making the changes you want would probably be very long. When you use templates, this code is shortened dramatically, as the templates are reused for any node matching the match expression. The nodes matching an expression are not limited to nodes with the same names. Expressions can be very complex and match a multitude of nodes, both elements and attributes. Because you use just one expression, this is a very powerful way of reusing code. In languages such as C, Java, and Visual Basic, you would have to write a series of statements for each node type that should match and explicitly call a procedure or function to deal with it. In XSLT, the processor performs all this work for you—a further reminder that XSLT is a declarative programming language.
Using templates, you can create stylesheets that can act on many different XML documents, even if they have different structures. Being able to do so doesn’t mean that you can process all XML documents with one stylesheet, but the structure of the data in a document doesn’t necessarily have to be a one-to-one match with data from another document. The nodes that are the same in both name and structure will, however, come out the same for each document. An example will show this point clearly. Listing 4.3 shows the stylesheet you can use.
Note
You can download the sample listings in this lesson from the publisher’s Web site.
ANALYSIS
Listing 4.3 contains several templates, operating on different elements. The template on line 5 matches the root of the source document and immediately continues processing so that other templates are matched. The template on line 9 matches any employee
element, outputs a value on line 10, and invokes templates for any child elements on line 11. The template on line 14 matching any car
element is not much different but outputs several attribute values, and then on line 19 the processing is continued again. The last template on line 22 outputs only the value of the number
attribute. It does not invoke other templates. Listing 4.4 shows a possible input for this stylesheet.
Listing 4.4 shows a list of cars. Each car element has a model
, manufacturer
, and year
attribute with different values for each element. If you apply the stylesheet in Listing 4.3 to it, the result is similar to Listing 4.5.
OUTPUT
ANALYSIS
The output from Listing 4.3 applied to Listing 4.4 is a nicely formatted list of cars. The template matching the car
element is responsible for this formatting. Note that for each car in the output, no plate
is listed because the elements in the source XML don’t have a plate attribute. Also, there is no output from the employee
and parkinglot
templates. No data matches them, so they aren’t called, and thus do not produce any output.
Listing 4.6 shows a different XML document that you can use with Listing 4.3.
ANALYSIS
The structure of the document in Listing 4.6 is different from Listing 4.4. It lists employees by name and adds information about the cars they have and which parking lot numbers they have. The car element is similar to that of Listing 4.4, but it adds an attribute for the license plate. Listing 4.7 shows the result when the stylesheet from Listing 4.3 (previously used with Listing 4.4) is used with Listing 4.6.
ANALYSIS
The result in Listing 4.7 is somewhat different. The information regarding the cars, however, is displayed in the same way in both Listings 4.5 and 4.7. The same template is matched for those elements, although the position of the car
elements within the XML document is entirely different. With information added about the employees and their parking lots, the templates operating on those elements are fired and this time around produce output. Also, now that the plate
attribute has been added, note that the car information in the output also contains the values of the license plates.
The preceding samples give you a good idea of how you can benefit from templates as reusable code across different data structures. The nice thing is that when you need to differentiate between the same element in differently structured data, you can. The match expressions created with XPath enable you to match with more precision if necessary, as shown in Listing 4.8.
ANALYSIS
Listing 4.8 shows two templates to replace the car
template on lines 14 through 20 in Listing 4.3. These templates make a difference between car
elements that are child nodes of a cars
element and of an employee
element. The template handling the latter hasn’t really changed, so the output would remain the same. The former, however, no longer tries to output the value of the plate
attribute, so the result looks like Listing 4.9.
OUTPUT
ANALYSIS
The result in Listing 4.9 is different because it no longer outputs the text Plate
: with each car. Listing 4.8 no longer outputs this text, and processing of the element is stopped after the value of the year
attribute is displayed. The processing stops because the template no longer uses apply-templates
to invoke new templates.
Working with Ad Hoc Data
The example in the preceding section is useful if you’re working with data that is structured somewhat ad hoc. A nice example is article text or something that contains headers, quotes, keywords, and so on tagged using XML. This document is much like HTML, but the tags tell what something is, not how to display it. The example in Listing 4.10 shows part of such a document.
ANALYSIS
Listing 4.10 shows a section of the preceding lesson. I tagged this text to show you what I mean by ad hoc structured data.
Note
The tagging I used in Listing 4.10 is fictional. This book was created differently, but it could be done this way.
Documents like Listing 4.10 have no predetermined structure. At one point or another, the text can contain keywords, code, or other kinds of text requiring tagging. These tags can even be nested; for example, you might nest a keyword inside a piece of code. This type of document is quite different from a document with product information or one like the samples in the preceding section. Those documents have a more or less predetermined (and somewhat rigid) structure.
Besides being reusable entities, templates enable you to work with flexible data. Currently, most languages don’t have this capacity, so it is one of the key benefits of XML and XSLT.
So far, you’ve seen some of the benefits of using templates, even though you haven’t learned about some of the more powerful template features. The samples used to this point work with single matches and let the processor decide which template to invoke. If necessary, you can impose control over that process. The following sections look at various ways to gain more control.
You have already learned a great deal about match templates and have seen that they are powerful tools. This power mainly comes from the matching expressions you can use. The expressions used so far have been quite simple, matching only nodes of the same type. You also can create expressions that match nodes of different types. The easiest way to do so is to create two expressions and use them together in one expression.
NEW TERM
You can add two expressions together by using the |
character, also known as the union operator. Listing 4.11 shows a stylesheet using the union operator.
Note
The union operator is not to be confused with the OR
operator common in some languages. When you’re matching templates, their function is more or less the same, but in XSLT, it can be used in other situations in which they are not similar.
The template on line 9 in Listing 4.11 operates on both the firstname
and lastname
elements. The expressions matching these elements are combined using the union operator. Listing 4.12 contains sample XML, and Listing 4.13 shows the result when Listing 4.11 is applied to Listing 4.12.
LISTING 4.12 Sample XML to Be Used with Listing 4.11
OUTPUT
ANALYSIS
The result in Listing 4.13 shows that the template matches both elements and does exactly the same with each element—outputs its value. Why is this technique useful? Well, an XML document may contain data similar to another, but named differently and possibly structured differently. The sample document in Listing 4.14 contains the same data as Listing 4.4 but is structured differently.
ANALYSIS
In Listing 4.14 the attributes of each car
element in Listing 4.4 are replaced by elements. So each car
element now has only child elements. These elements have the same names and values as the attributes in Listing 4.4. The challenge is now getting (more or less) the same output from both documents by using only one style-sheet. To do so, you need to create templates that match both attributes and elements for each element/attribute name. For instance, the match expression for the model
element/attribute would be model|@model
, so the model
attribute is treated the same as the model
element.
There is one problem. For Listing 4.14, this naming technique works fine because this example consists of elements only. When xsl:apply-templates
is executed, the processor matches all elements it encounters. Attributes, however, are not matched, so applying these templates to Listing 4.4 would yield an empty result because all data is stored in attributes. You therefore need to specify that attributes should also be added to the node-set that the processor tries to match. To do so, you need to add a select
attribute to the xsl:apply-templates
element. This attribute’s value should tell the processor which node-set to use when looking for a match. Like the match
attribute of a template, the value of a select
attribute is an XPath expression.
Be aware that a processor handles a template match expression and a select expression differently. As I discussed earlier, a template is matched and therefore depends on the node’s place and sequence in the source document. A select expression, on the other hand, selects a node-set. The node sequence in this case depends on the select expression defining the node-set. You used select expressions previously with the xsl:value-of
element. You may remember that these expressions are somewhat awkward when a select expression yields a node-set instead of a text value. The expression has to create the value of the selected node-set. That same node-set is what you select when you’re using xsl:apply-templates
. The idea is that you select the node-set that has to be matched, and then the processor takes over and starts matching each node in the node-set against the available templates. Listing 4.15 shows how you can transform Listings 4.4 and 4.14 with the same stylesheet.
Listing 4.15 produces a result similar to Listing 4.9 for both Listings 4.4 and 4.14. The templates on lines 13, 17, and 21 match either an element or an attribute (with the same name). They therefore perform the same action on the context node, making no difference between an element and an attribute. Line 10 in Listing 4.15 is very important because it tells the processor to select a node-set consisting of all the elements and attributes that are child nodes of the context node. In this case, all elements that are child elements of the car
element and all attributes of the car
element are part of the selected node-set. For Listing 4.4, this node-set consists solely of attributes, and for Listing 4.14, it consists solely of elements, but that makes no difference for the output.
Line 10 in Listing 4.15 contains a broad selection of all elements and attributes. You can easily limit this line to only certain elements and attributes. For instance, if you want a list of models only, you can change the value of the select expression to model|@model
. This select expression selects only the child nodes that are either model
elements or model
attributes.
NEW TERM
Templates don’t necessarily have to be matched. They can be called directly as well, forcing the processor to execute the template. Using templates this way is much more like traditional programming in which you call a function or procedure that is to be executed. For you to be able to call a template explicitly, it must have a unique name within the stylesheet. This is why these types of templates are referred to as named templates. A named template is a template that can be called from another template, rather than being matched to a node by the processor.
You name a template by adding a name
attribute with a unique identifier as its name. This identifier may contain a namespace, but if that’s the case, that namespace must be declared either in the stylesheet
element or as part of the template itself.
Note
Namespaces are discussed thoroughly on Day 15, “Working with Name-spaces”, so don’t worry if you’re not familiar with them.
A big difference between match templates and named templates is that when a named template is called, the context node stays the same. With a matched template, the context node changes the moment xsl:apply-templates
is used. Throughout the matching process, the context node can even change again.
Named templates are invoked using the xsl:call-template
element. Which template is invoked is determined by the name
attribute of xsl:call-template
. The name
attribute’s value must be the name of an existing template within the stylesheet. If the template doesn’t exist, an error occurs. Named and match templates do not interfere with one another. It is perfectly acceptable to have a template with a name
attribute that is the same as a match
attribute of another. Listing 4.16 shows an example with a named template.
ANALYSIS
NEW TERM
Listing 4.16 contains a template matching the car
element on line 9, and it also contains a template named car
on line 13. The named template is called from the matched car
template, at which time control is passed over to the named template without changing the context node. Hence, the xsl:value-of
elements can use relative addressing and directly address the attributes of the car
element without having to point to the location of those attributes. Even though the flow of control is entirely different for this stylesheet, it yields an output similar to Listing 4.9 when it is applied to Listing 4.4. The flow of control is the sequence in which commands or, in the case of XSLT, elements are executed.
Listing 4.16 is, of course, not very useful. The car
element is processed by a different template that does exactly the same as match templates of earlier samples. Why are these templates useful? Well, mostly to break up complex templates into smaller units that are easier to work with.
NEW TERM
Named templates give you complete control over the flow of control. This undermines the whole idea of the data-driven nature of XSLT. However, in some cases, you might need to impose such control. One of those instances occurs when you need recursion, which happens when a function or, in the case of XSLT, a template calls itself. This is often the case when operations need to be executed several times based on some condition.
Note
On Day 17, “Using Recursion,” recursion will be discussed further.
A match template and a named template can be one and the same. A template must have a match expression or a name, but can also have both. In that case, the template can be either matched or called, whichever is appropriate at that point.
In the preceding sections, you learned that you can explicitly tell the processor which template to process. You also learned that you can impose some control over which templates can be matched. But what happens when a node is matched by more than one template? When more than one template applies, their precedence rules determine which template is actually used. You can influence the precedence using priorities. You also can make different templates for different uses. You determine which template is used in which instance. In the following sections, these topics will be discussed.
You will find that in some cases you may have to go through a document more than once, each time creating different output. In these situations, you need several different templates matching the same nodes. Each template takes a different action. The problem is that the processor cannot determine which template to use in the separate instances. To ensure that the processor invokes the correct template, you can add a mode
attribute to each template. The value of the mode
attribute can be any name, but it needs to be different for each template matching the same node or node-set, and the same for each template to be invoked in a mode. When you xsl:apply-templates
, you specify which mode to use. The processor then matches using only those templates belonging to that particular mode. Using this approach, you can ensure that each time you go through the document different tasks will be performed, as shown in Listing 4.17.
The stylesheet in Listing 4.17 contains two different modes, invoked one after another. Line 6 invokes templates using the index
mode, and line 7 invokes templates using the info
mode. When each mode is invoked, only the templates that are part of that particular mode are matched. This way, the document is processed twice, each time with a different mode. Line 10 defines a template that uses the index
mode. This template is invoked only by line 6. The template on line 14 uses the info
mode and is invoked only by line 7. Listing 4.18 shows the result of applying Listing 4.17 to Listing 4.4.
In Listing 4.18, the document from Listing 4.4 is processed twice. The first time only the car models are listed. On the second run, each car is listed by model, and the manufacturer and year are added.
Using modes is useful when you need to create a document that starts with a table of contents and also contains the entire text or all the data. For example, you see Web pages where the start of the page has a table of contents that enables you to go directly to the section of the page that you’re interested in. Modes are not necessarily restricted to the entire document. You also can use modes on just a section of a document tree. You can even change modes from one template to another. This way, you can create a complex flow of control, creating complex output.
Modes can also help you when you have different formatting for differently structured XML sources. In that case, you can create a stylesheet that detects which structure is used and then use the appropriate mode to process the document. This way, nodes that have to be processed differently for that structure are treated differently. Be aware that only templates belonging to a certain mode are matched when that mode is used. Templates that do not have a mode
attribute are treated as a separate mode. This means that templates that have no mode are matched only when no mode is used.
Keeping templates apart using modes is very handy, but when templates with the same mode match, you need to know which template will actually be invoked. The processor selects the actual template that is invoked by going through a series of selection steps as follows:
1. The processor selects all the templates that have a match
attribute.
2. From those templates, the processor selects all templates that have the same mode as the active mode.
3. The processor selects those templates whose match expression matches the current node.
4. If more than one template has a match expression that matches the current node, the template that has the highest numerical priority is selected.
5. If more than one template is still left, the processor can report an error or use the last template that occurs in the stylesheet.
The priority in step 4 is a positive or negative number. A template’s priority can be specified explicitly. If it is not, it is calculated based on the match expression. Typically, match expressions that are more precise have a higher numerical priority. For instance, an expression using a wildcard has a lower priority than a match expression specifically matching a node. Multiple expressions added to one another using the union operator (|
) are treated as separate expressions. For each expression, the priority is calculated separately. Table 4.1 shows the default priorities.
Table 4.1 looks more complex than it is. Basically, it shows that the less specific an expression is, the lower its priority. Expressions with node tests that are outside the current axis or with predicates have a higher priority because the expression more specifically states which node is to be matched. Note that this also means that an expression such as //node ()
has a higher priority than an expression that specifies a child element. This prioritization is somewhat strange because the former expression actually matches any node within the document tree. This example goes against the idea that the more precise an expression is, the higher the priority will be. Listing 4.19 gives you an idea of the default priorities.
ANALYSIS
The template on line 17 uses a wildcard to match elements. Theoretically, it will match any element with any name. The templates on lines 9 and 13 match specific elements and should have precedence over the wildcard template. If you apply Listing 4.19 to Listing 4.4, the default priorities ensure that the template matching the car
element is invoked for the car
elements in Listing 4.4. If that weren’t the case, the template on line 17 using a wildcard would be invoked instead. Listing 4.20 shows the result of applying Listing 4.19 to Listing 4.4.
OUTPUT
In Listing 4.20, the template matching the car
element is invoked for each car
element in Listing 4.4. The template with the wildcard match isn’t invoked anywhere. If the listing didn’t have any priorities, the template with the wildcard would be the last template to occur in the stylesheet and hence would be the template invoked. Because of the default priority, the template matching the car
element is invoked as it should be.
As your stylesheets become larger, default priorities will be more and more important. You are likely to encounter situations in which the invoked template is different from the template you thought would be invoked. In those cases, an XSLT debugger is useful because it shows which template is invoked. Getting the processor to invoke the right templates at that point may prove tricky, but don’t let the difficulty slow you down. When you gain more experience using XSLT, you will learn to avoid these situations most of the time and remedy them quickly when they do occur.
The default priorities calculated by the processor are always between -0.5 and 0.5. This leaves you free to define priorities that override the default priorities. If you define a priority, it needs to be a whole number, such as 1, 5, or 13. Specifying a priority with a value lower than -0.5 would make no sense because it would never be invoked; the default priority would always be higher.
You can specify your own priority for a template by adding a priority
attribute to the xsl:template
element. This attribute needs to have a value that is a positive whole number. Listing 4.21 shows a stylesheet using priorities.
Listing 4.21 is similar to Listing 4.19 except that line 17 defines a template with a priority
attribute. The value of the priority
attribute is 1
, so it will override any default priorities. So, even if the match expression of the other templates is more specific, the wildcard template on line 17 will be invoked. Listing 4.22 shows the result of applying Listing 4.21 to Listing 4.4.
OUTPUT
ANALYSIS
The output in Listing 4.22 is short because the cars
element in Listing 4.4 is matched by the template on line 17 in Listing 4.21 using the wildcard match instead of the template matching the cars
element specifically on line 9. Because this template doesn’t contain the xsl:apply-templates
element, no further processing occurs after this template is invoked and the value xyz
is written to the output.
From the example in Listing 4.22, you may think that defined priorities are not very useful. This is because of the simplicity of the XML documents and the stylesheets used so far. In more complex documents and stylesheets, priorities are more valuable—specifically in cases in which the match expressions are general and more than one template matches. If you have a specific matching template, you don’t have a problem. However, if all matching templates are quite general, you don’t know for certain which template matches a certain node. One solution would be to write expressions that are less general. However, doing so would mean writing more code for the same task because you would probably have to create more templates for that task. Therefore, priorities become useful when you have a very wide base of code reuse.
Today you learned that templates are structures that can be filled with data. Throughout a stylesheet, templates can be used as blocks of reusable code. Match expressions determine for which elements and attributes a template is used. These match expressions can be very complex, and one expression can even be the union of multiple expressions.
If more than one template matches the current node, the processor takes a series of steps to determine which template to invoke. These steps involve selecting the templates of the current mode that match the node and using the defined or calculated priority. The general rule for calculated priorities is that the more precisely an expression matches the current node, the higher the priority. Because of the way these rules work, you may have a surprise or two.
Today you also learned that you can invoke templates by calling them rather than have the processor match a template. This way, you can break up the complex code of a matched template because the context node stays the same when you call another template. A template must have a match expression or a name to be called with, but may have both.
Tomorrow you will learn how to create more complex output that can contain text with elements and attributes. This way, you can create XML documents from other XML documents and, in the process, restructure the XML.
Q Can I create a stylesheet without templates?
A If you want something to happen in a controlled fashion, you need at least a template matching the root of the XML document. If you can create all the output from the xsl:value-of
elements, that is up to you. You will learn more details about such elements in the following lessons, but because using templates is the preferred method and key to XSLT, templates have been discussed thoroughly first.
Q Can I use modes to create multiple documents?
A Just using modes is not enough to create multiple documents. You can create output with a divider and split the resulting document into more documents by using another program.
Q Can called templates have a mode?
A No. Named templates need to have a unique name within the document. Hence, differentiating between them with modes makes no sense.
This workshop tests whether you understand all the concepts you learned today. It is helpful to know and understand the answers before starting tomorrow’s lesson. You can find the answers to the quiz questions and exercises in Appendix A.
1. True or False: A template can have a match
attribute and a name
attribute.
2. True or False: A template using a wildcard expression in the current context has a higher priority than an expression targeting a specific node in the current context.
3. If xsl:apply-templates
is used with a mode, will templates that have no mode be matched?
4. Why do you use a select
attribute with xsl:apply-templates
?
5. What is the difference between selecting nodes and matching nodes?
1. Create a stylesheet for Listing 4.4 to display a list of models, then a list of manufacturers, and finally a list of years.
2. Create a stylesheet with the same output as Exercise 1, but for Listing 4.14. You also may change the stylesheet from the preceding exercise to handle both listings.
18.191.254.44