facebook google plus twitter
Webucator's Free XSLT 1.0 and XPath 1.0 Tutorial

Lesson: Templates, Parameters and Variables

Welcome to our free XSLT 1.0 and XPath 1.0 tutorial. This tutorial is based on Webucator's XSLT 1.0 and XPath 1.0 Training course.

Lesson Goals

  • To use xsl:apply-templates and xsl:call-template.
  • To pass parameters to templates.
  • To use template modes.
  • To understand how templates are prioritized.
  • To create and use variables.

xsl:apply-templates

Most XSLT documents contain more than one <xsl:template> tag. The <xsl:apply-templates /> tag is used to indicate that the XSLT processor should look deeper into the XML document and, at each step, check the XSLT document to see if there is a matching template for the current element.

The following is a simple course outline marked up as XML.

Code Sample:

TemplatesParametersVariables/Demos/Xml101.xml
<?xml version="1.0"?>
<?xml-stylesheet href="CourseAsList.xsl" type="text/xsl"?>
<course>
	<head>
		<title>Introduction to XML</title>
		<course_num>XML101</course_num>
		<course_length>3 days</course_length>
	</head>
	<body>
		<prerequisites>
			<prereq>Experience with Word Processing</prereq>
			<prereq optional="true">Experience with HTML</prereq>
			<prereq>Experience with computers</prereq>
		</prerequisites>
		<outline>
			<topics>
				<title>XML Basics</title>
				<topics>
					<title>What is XML?</title>
					<title>XML Benefits</title>
					<topics>
						<title>XML Holds Data, Nothing More</title>
						<title>XML Separates Structure from Formatting</title>
						<title>XML Promotes Data Sharing</title>
						<title>XML is Human-Readable</title>
						<title>XML is Free</title>
					</topics>
				</topics>
				<topics>
					<title>XML Documents</title>
					<topics>
						<title>The Prolog</title>
						<title>Elements</title>
						<title>Attributes</title>
						<title>CDATA</title>
						<title>XML Syntax Rules</title>
						<title>Special Characters</title>
					</topics>
				</topics>
				<topics>
					<title>Creating a Simple XML File</title>
				</topics>
			</topics>
		</outline>
	</body>
	<foot>
		<creator>Josh Lockwood</creator>
		<date_created>2011-07-25</date_created>
		<modifications madeby="Colby Germond" date="2011-05-05">
			<modification type="insert">Added HTML prerequisite</modification>
			<modification type="edit">Fixed some typos</modification>
		</modifications>
		<modifications madeby="Ted Ferris" date="2011-05-05">
			<modification type="insert">Added prerequisite: Experience with 
				Computers</modification>
			<modification type="insert">Added XML Documents and Creating
				a Simple XML Document Topics</modification>
		</modifications>
	</foot>
</course>

Notice the xml-stylesheet processing instruction pointing to CourseAsList.xsl. Let's look at that file.

Code Sample:

TemplatesParametersVariables/Demos/CourseAsList.xsl
<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="html" doctype-public="-//W3C//DTD XHTML 1.0 Strict//EN" doctype-system="http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd" indent="yes"/>
<xsl:template match="/">
	<html>
		<head>
			<title><xsl:value-of select="/course/head/title"/></title>
			<style type="text/css">
				#outline { font-size:larger }
				ul ul {font-size: smaller }
			</style>
		</head>
		<body>
		<h1><xsl:value-of select="/course/head/title"/></h1>
		<div id="courseNum"><xsl:value-of select="/course/head/course_num"/></div>
		<div id="courseLength">
			<xsl:value-of select="/course/head/course_length"/>
		</div>
		<xsl:apply-templates select="/course/body/prerequisites"/>
		<xsl:apply-templates select="/course/body/outline"/>
		</body>
	</html>
</xsl:template>

<xsl:template match="prerequisites">
	<h2>Prerequisites</h2>
	<ul>
	<xsl:for-each select="prereq">
		<li>
			<xsl:value-of select="." />
			<xsl:if test="@optional = 'true'">
				(optional, but recommended)
			</xsl:if>
		</li>
	</xsl:for-each>
	</ul>
</xsl:template>

<xsl:template match="outline">
	<h2>Course Outline</h2>
	<div id="outline">
		<xsl:apply-templates />
	</div>
</xsl:template>

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

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

</xsl:stylesheet>

As you can see, this file uses multiple <xsl:template> tags to match different elements in the XML file. Each time one of the specified elements is matched, the matching template comes into play. The <xsl:apply-templates /> tags with no select attribute tell the XSLT processor to continue looking through the XML document for any node matched in a template.

If an element found in the source document has no matching template, the default XSLT template, which simply outputs the text content of the element, is used.

Using xsl:apply-templates

Duration: 15 to 25 minutes.

In this exercise, you will practice using xsl:apply-templates.

  1. Open TemplatesParametersVariables/Exercises/BeatlesTemplates.xsl.
  2. There is already an xsl:template that matches the root node. Within this template, add two xsl:apply-templates, one that matches real Beatles and one that matches fake Beatles.
  3. After the initial xsl:template, create new templates for the beatle elements and for firstname and lastname elements.
    • Note that there should separate templates for real Beatles and for fake Beatles.
    • The real beatles should be enclosed in <Real></Real> tags.
    • The fake beatles should be enclosed in <Fake></Fake> tags.
    • The first names should be enclosed in <FirstName></FirstName> tags.
    • The last names should be enclosed in <LastName></LastName> tags.
  4. To test your solution, transform TemplatesParametersVariables/Exercises/BeatlesTemplates.xml against TemplatesParametersVariables/Exercises/BeatlesTemplates.xsl. The output is below.

Code Sample:

TemplatesParametersVariables/Exercises/TemplatesOutput.xml
<?xml version="1.0" encoding="UTF-8"?>
<Beatles>
	<RealBeatles>
		<Real>
			<FirstName>Paul</FirstName>
			<LastName>McCartney</LastName>
		</Real>
		<Real>
			<FirstName>John</FirstName>
			<LastName>Lennon</LastName>
		</Real>
		<Real>
			<FirstName>George</FirstName>
			<LastName>Harrison</LastName>
		</Real>
		<Real>
			<FirstName>Ringo</FirstName>
			<LastName>Starr</LastName>
		</Real>
	</RealBeatles>
	<FakeBeatles>
		<Fake>
			<FirstName>Nat</FirstName>
			<LastName>Dunn</LastName>
		</Fake>
	</FakeBeatles>
</Beatles>

Solution:

TemplatesParametersVariables/Solutions/BeatlesTemplates.xsl
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" 
				xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
	<xsl:output method="xml" indent="yes"/>
	<xsl:template match="/">
		<Beatles>
			<RealBeatles>
				<xsl:apply-templates select="beatles/beatle[not(@real='no')]"/>
			</RealBeatles>
			<FakeBeatles>
				<xsl:apply-templates select="beatles/beatle[@real='no']"/>
			</FakeBeatles>
		</Beatles>
	</xsl:template>
	<xsl:template match="beatle[not(@real='no')]">
		<Real><xsl:apply-templates/></Real>
	</xsl:template>
	<xsl:template match="beatle">
		<Fake><xsl:apply-templates/></Fake>
	</xsl:template>
	<xsl:template match="firstname">
		<FirstName><xsl:apply-templates/></FirstName>
	</xsl:template>
	<xsl:template match="lastname">
		<LastName><xsl:apply-templates/></LastName>
	</xsl:template>
</xsl:stylesheet>

xsl:call-template

The xsl:call-template element is used to call a named template. The following example illustrates how it is used.

Code Sample:

TemplatesParametersVariables/Demos/BeatlesCallTemplates.xsl
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" 
				xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
	<xsl:output method="xml" indent="yes"/>
	<xsl:template match="/">
		<xsl:call-template name="StartHTML"/>
	</xsl:template>
	<xsl:template name="StartHTML">
		<html>
			<head>
				<title>Beatles</title>
			</head>
			<body>
				<h1>Beatles</h1>
				<ul>
					<xsl:apply-templates select="beatles/beatle">
						<xsl:sort select="lastname"/>
					</xsl:apply-templates>
				</ul>
			</body>
		</html>
	</xsl:template>
	<xsl:template match="beatle[not(@real='no')]">
		<li style="color:blue;"><xsl:apply-templates/></li>
	</xsl:template>
	<xsl:template match="beatle">
		<li style="color:red; text-decoration:line-through">
			<xsl:apply-templates/>
		</li>
	</xsl:template>
	<xsl:template match="name">
		<xsl:value-of select="lastname"/>,
		<xsl:value-of select="firstname"/>
	</xsl:template>
</xsl:stylesheet>

The template being called is named StartHTML:

<xsl:template name="StartHTML">

The xsl:call-template tag also takes the name attribute, which simply matches the name of the template started above:

<xsl:call-template name="StartHTML"/>

The called template inherits the context from the calling template. In this case the calling template matched the root node. Therefore, the root node will be the context node for all relative XPaths in the called template.

Passing Parameters

XSLT templates can accept parameters, which can be passed from xsl:apply-templates and xsl:call-template. The following code illustrates this.

Code Sample:

TemplatesParametersVariables/Demos/BeatlesCallTemplates2.xsl
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" 
				xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
	<xsl:output method="xml" indent="yes"/>
	<xsl:template match="/">
		<xsl:call-template name="StartHTML">
			<xsl:with-param name="Title" select="'Beatles'"/>
		</xsl:call-template>
	</xsl:template>
	<xsl:template name="StartHTML">
		<xsl:param name="Title" select="'Untitled'"/>
		<html>
			<head>
				<title><xsl:value-of select="$Title"/></title>
			</head>
			<body>
				<h1><xsl:value-of select="$Title"/></h1>
				<ul>
					<xsl:apply-templates select="beatles/beatle">
						<xsl:sort select="lastname"/>
					</xsl:apply-templates>
				</ul>
			</body>
		</html>
	</xsl:template>
	<xsl:template match="beatle[not(@real='no')]">
		<li style="color:blue;"><xsl:apply-templates/></li>
	</xsl:template>
	<xsl:template match="beatle">
		<li style="color:red; text-decoration:line-through">
			<xsl:apply-templates/>
		</li>
	</xsl:template>
	<xsl:template match="name">
		<xsl:value-of select="lastname"/>,
		<xsl:value-of select="firstname"/>
	</xsl:template>
</xsl:stylesheet>

The xsl:call-template element looks like this:

<xsl:call-template name="StartHTML"> <xsl:with-param name="Title" select="'Beatles'"/> </xsl:call-template>

The nested xsl:with-param element is used to pass a value for Title to the StartHTML template, which looks like this:

<xsl:template name="StartHTML"> <xsl:param name="Title" select="'Untitled'"/> <html> <head> <title><xsl:value-of select="$Title"/></title> </head> <body> <h1><xsl:value-of select="$Title"/></h1> <ul> <xsl:apply-templates select="beatles/beatle"> <xsl:sort select="lastname"/> </xsl:apply-templates> </ul> </body> </html> </xsl:template>

The xsl:param element is used to receive the value passed in. Notice that the names used in the xsl:with-param and in the xsl:param elements are the same. This is required as multiple parameters could be passed in with different names.

This xsl:param element also has a select attribute. This is the default value in the event that no value for Title is passed in.

The Title parameter is referenced twice, once in the HTML title element and once in the HTML h1 element. Notice that it is preceded by a dollar sign ($) when referenced. This tells the XSLT processor that it is variable rather than an XPath.

Another thing to notice is that xsl:with-param is passing in a literal string for Title. Notice the single quotes around Beatles in select="'Beatles'". It could just as easily pass in an XPath if the title were to be dynamically retrieved from the XML source document.

Using xsl:call-template

Duration: 10 to 15 minutes.

In this exercise, you will practice passing parameters to named templates.

  1. Open TemplatesParametersVariables/Exercises/CourseAsList.xsl.
  2. Below the first template, which matches the root, create a new template name StartHTML.
  3. Move the entire contents of the first template into the new template you just created.
  4. Add an xsl:call-template element to the first template that calls the StartHTML template and passes it the value of the title element from the source XML document.
  5. Modify the StartHTML template so that it accepts the passed-in parameter and uses it in the nested HTML title and h1 elements.
  6. To test your solution, transform TemplatesParametersVariables/Exercises/XML101.xml against TemplatesParametersVariables/Exercises/CourseAsList.xsl.

Solution:

TemplatesParametersVariables/Solutions/CourseAsList.xsl
<?xml version="1.0"?>
<xsl:stylesheet version="1.0" 
				xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="html"/>
<xsl:template match="/">
	<xsl:call-template name="StartHTML">
		<xsl:with-param  name="Title" select="course/head/title"/>
	</xsl:call-template>
</xsl:template>

<xsl:template name="StartHTML">
	<xsl:param name="Title" select="'Untitled'"/>
	<html>
		<head>
			<title><xsl:value-of select="$Title"/></title>
			<style type="text/css">
				#outline { font-size:larger }
				ul ul {font-size: smaller }
			</style>
		</head>
		<body>
		<h1><xsl:value-of select="$Title"/></h1>
		<div id="courseNum">
			<xsl:value-of select="/course/head/course_num"/>
		</div>
		<div id="courseLength">
			<xsl:value-of select="/course/head/course_length"/>
		</div>
		<xsl:apply-templates select="/course/body/prerequisites"/>
		<xsl:apply-templates select="/course/body/outline"/>
		</body>
	</html>
</xsl:template>

<xsl:template match="prerequisites">
	<h2>Prerequisites</h2>
	<ul>
	<xsl:for-each select="prereq">
		<li>
			<xsl:value-of select="." />
			<xsl:if test="@optional = 'true'">
				(optional, but recommended)
			</xsl:if>
		</li>
	</xsl:for-each>
	</ul>
</xsl:template>

<xsl:template match="outline">
	<h2>Course Outline</h2>
	<div id="outline">
		<xsl:apply-templates />
	</div>
</xsl:template>

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

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

</xsl:stylesheet>

Removing Content

As the XSLT processor navigates through a source XML document, it looks for nodes that match templates in the XSLT document. If it finds a node that has no matching template, it uses the default XSLT template, which usually outputs the text inside the element and continues its search.

To illustrate this, let's look at TemplatesParametersVariables/Demos/FirstName.xml and TemplatesParametersVariables/Demos/FirstName.xsl below.

Code Sample:

TemplatesParametersVariables/Demos/FirstName.xml
<?xml version="1.0"?>
<?xml-stylesheet href="FirstName.xsl" type="text/xsl"?>
<person>
	<name>
		<firstname>Paul</firstname>
		<lastname>McCartney</lastname>
	</name>
	<job>Singer</job>
	<gender>Male</gender>
</person>

Code Sample:

TemplatesParametersVariables/Demos/FirstName.xsl
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" 
				xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
	<xsl:output method="text"/>
	<xsl:template match="firstname">
		We found a first name! 
	</xsl:template>
</xsl:stylesheet>

The XSLT processor starts at the top and navigates its way through the XML document looking for templates for each node. However, this XSLT only has a template for the firstname node. So, when the XSLT processor finds the other nodes, it simply outputs the contained text. The result is shown below.

We found a first name! McCartneySingerMale

We must explicitly tell the XSLT processor not to output anything for the lastname, job and gender elements if we want their values not to appear. This is accomplished by creating an empty template matching these elements as shown below.

Code Sample:

TemplatesParametersVariables/Demos/FirstNameOnly.xsl
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" 
				xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
	<xsl:output method="text"/>
	<xsl:template match="firstname">
		We found a first name! 
	</xsl:template>
	<xsl:template match="lastname | job | gender"/>
</xsl:stylesheet>

Notice that the last xsl:template is an empty tag (i.e, it doesn’t contain anything). This tells the XSLT processor that when the elements indicated by the match attribute are found, nothing should be output. The pipes (|) are uses to separate XPaths.

Template Modes

Sometimes it will be necessary for a stylesheet to visit a node multiple times outputting different results each time. For example, imagine an XML document for a book made up of many chapters. The XSLT might output the chapter titles in the table of contents as well as at the beginning of each chapter. You could do this by having two templates that match book/chapter. However, the processor has to be able to distinguish between the two templates, so it knows when to use which. This can be accomplished by applying the mode attribute to the relevant xsl:apply-templates and xsl:template tags. The files below illustrate how mode is used.

Code Sample:

TemplatesParametersVariables/Demos/BeatlesMode.xml
<?xml version="1.0"?>
<?xml-stylesheet href="BeatlesMode.xsl" type="text/xsl"?>
<beatles>
<beatle link="http://www.paulmccartney.com" image="Images/Paul.jpg">
	<name>
		<firstname>Paul</firstname>
		<lastname>McCartney</lastname>
	</name>
	<bio src="http://www.imdb.com/name/nm0005200/bio">Played bass guitar for 
The Beatles in the 1960s. Considered the most successful pop composer 
of all time. In the "Guinness Book of World Records" for most records
sold, most #1s (shared) and largest paid audience for a concert (350,000+
people, 1989 in Brazil). After the Beatles, he formed Wings, one of the most 
commercially successful groups of the 1970s. Post-70s solo career has been 
sporadic in both commercial and artistic terms. Successes include albums 
"Tug of War" and "Flowers in the Dirt". Flops include movie Give My Regards 
to Broad Street (1984) and album "Press to Play".</bio>
</beatle>
<beatle link="http://www.johnlennon.com" image="Images/John.jpg">
	<name>
		<firstname>John</firstname>
		<lastname>Lennon</lastname>
	</name>
	<bio src="http://www.imdb.com/name/nm0006168/bio">John Winston 
	(later Ono) Lennon was born on October 9, 1940, in Liverpool England.
	In the mid 1950s he formed his first band The Quarrymen (after Quarry 
	Bank High School, which he attended), who with the addition of Paul 
	McCartney and George Harrison, later became the Beatles. After some 
	years of performing in Liverpool and Hamburg Germany, Beatlemania 
	erupted in England and Europe in 1963 after the release of their singles 
	"Love Me Do" and "Please Please Me". The next year the Beatles 
	flew to America to appear on the Ed Sullivan Show, and 
	Beatlemania spread worldwide...</bio>
</beatle>
<beatle link="http://www.georgeharrison.com" image="Images/George.jpg">
	<name>
		<firstname>George</firstname>
		<lastname>Harrison</lastname>
	</name>
<bio src="http://www.rollingstone.com/artist/bio/_/id/55146/georgeharrison">
	The youngest member of the Beatles, George Harrison was often 
	considered "the shy one." In fact, his strong voice was arguably the 
	most underrated of the four. Lennon and McCartney often passed 
	over many of Harrison's song submissions, recording only a small 
	selection of his masterpieces per album (including "While My Guitar 
	Gently Weeps" and "Here Comes The Sun"). When Harrison's first solo
	recording, All Things Must Pass, was released, many music critics
	felt that the triple album's fruitfulness was a direct result of Lennon 
	and McCartney's neglect. In his work with the Beatles and solo, 
	Harrison blended earthy Roots tones with droning eastern influences 
	and cascading melodies. Compared to Lennon's barbed and bluesy 
	grit or McCartney's jaunty pop sensibilities, the sound was organic 
	and blooming, huge yet worldly. His songs unfolded unpredictably 
	and took the listener to unfamiliar sonic regions. Music enthusiasts 
	credit him for integrating eastern sounds into western music -- Harrison 
	introduced the sitar and Indian ragas to pop music. He also 
	spearheaded the Concert for Bangladesh in 1971, a music festival 
	assembled to benefit Bengali refugees of the India-Pakistan war. 
	Harrison passed away on Nov. 29, 2001, in Los Angeles after 
	losing a prolonged battle with cancer.</bio>
</beatle>
<beatle link="http://www.ringostarr.com" image="Images/Ringo.jpg">
	<name>
		<firstname>Ringo</firstname>
		<lastname>Starr</lastname>
	</name>
	<bio src="http://www.imdb.com/name/nm0823592/bio">Popular drummer, Ringo 
	Starr was born Richard Starkey in Liverpool, Merseyside, England, on 
	July 7, 1940.	His parents were divorced when he was 3 and her mother 
	and Richard moved to another home of the same city. While attending 
	Silas Infants' Schools, he was suffering the many diseases which ruined 
	his education. Growing up, he was suffering from severe abdominal pains. 
	He was also diagnosed with a ruptured appendix that led to an inflamed
	peritoneum, that also led to one of his first surgeries. He was in a coma 
	for a couple of months during which more operations 
	were available, and he was known to be accident prone...</bio>
</beatle>
</beatles>

Code Sample:

TemplatesParametersVariables/Demos/BeatlesMode.xsl
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" 
				xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
	<xsl:output method="xml" indent="yes"/>
	<xsl:template match="/">
		<html>
			<head>
				<title>The Beatles</title>
			</head>
			<body>
				<h1 align="center">THE BEATLES</h1>
				<div align="center">
					<xsl:apply-templates mode="links"/>
				</div>
				<hr/>
				<xsl:apply-templates mode="body"/>
			</body>
		</html>
	</xsl:template>
	<xsl:template match="beatle" mode="links">
		<a href="#{name/firstname}"><img src="{@image}" border="0"
				title="{name/firstname} {name/lastname}"/></a>
	</xsl:template>
	<xsl:template match="beatle" mode="body">
		<a name="{name/firstname}"></a>
		<h2>
			<xsl:value-of select="name/firstname"/>
			<xsl:text> </xsl:text>
			<xsl:value-of select="name/lastname"/>
		</h2>
		<p><xsl:value-of select="bio"/></p>
	</xsl:template>
</xsl:stylesheet>

The first xsl:apply-templates instructs the processor to continue to look through the XML source document for nodes that match any templates with the mode set to "links". The second xsl:apply-templates instructs the processor to continue to look through the XML source document again for nodes that match any templates with the mode set to "body". The result is shown in TemplatesParametersVariables/Demos/BeatlesModeOutput.html, which looks like this in a browser.

Let's take a look at one beatle element in the XML document and the two relevant templates in the XSLT and the associated xsl:apply-templates tags.

<beatle link="http://www.paulmccartney.com" image="Images/Paul.jpg"> <name> <firstname>Paul</firstname> <lastname>McCartney</lastname> </name> <bio src="http://www.imdb.com/name/nm0005200/bio"> Played bass guitar... ... ...and album "Press to Play"...</bio> </beatle>
<div align="center"> <xsl:apply-templates mode="links"/> </div> <hr/> <xsl:apply-templates mode="body"/> <xsl:template match="beatle" mode="links"> <a href="#{name/firstname}"><img src="{@image}" border="0" title="{name/firstname} {name/lastname}"/></a> </xsl:template> <xsl:template match="beatle" mode="body"> <a name="{name/firstname}"></a> <h2> <xsl:value-of select="name/firstname"/> <xsl:text> </xsl:text> <xsl:value-of select="name/lastname"/> </h2> <p><xsl:value-of select="bio"/></p> </xsl:template>

Here is how the processor steps through this transformation.

  1. Finds first xsl:apply-templates tag with mode set to "links" and continues to look through the source XML document's nodes starting from the root.
  2. Finds first beatle node in XML document and applies matching template with the mode set to "links". This creates HTML code that outputs a picture of Paul McCartney linked to an anchor on the same page.
  3. Repeats step 2 for all other beatle nodes in the XML document.
  4. Finds second xsl:apply-templates tag with mode set to "body" and continues to look through the source XML document's nodes starting from the root.
  5. Finds first beatle node in XML document and applies matching template with the mode set to "body". This create HTML code that includes an anchor named "Paul", a "Paul McCartney" heading, and Paul's bio.
  6. Repeats step 5 for all other beatle nodes in the XML document.

Template Priority

Take a look at the following code samples:

Code Sample:

TemplatesParametersVariables/Demos/DefaultPriority.xml
<?xml version="1.0"?>
<?xml-stylesheet href="DefaultPriority.xsl" type="text/xsl"?>
<person>
	<name>
		<firstname>Paul</firstname>
		<lastname>McCartney</lastname>
	</name>
	<job>Singer</job>
	<gender>Male</gender>
</person>

Code Sample:

TemplatesParametersVariables/Demos/DefaultPriority.xsl
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" 
				xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text"/>
	
<xsl:template match="firstname">
	<xsl:value-of select="name()"/>: We found a first name! 
</xsl:template>
	
<xsl:template match="firstname[following-sibling::lastname]">
	<xsl:value-of select="name()"/>: 
		We found a first name followed by a last name! 
</xsl:template>

<xsl:template match="*">
	<xsl:value-of select="name()"/>: We found an element! 
	<xsl:apply-templates/>
</xsl:template>

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

Code Sample:

TemplatesParametersVariables/Demos/DefaultPriorityOutput.xml
person: We found an element! 
	name: We found an element! 
	firstname: We found a first name followed by a last name! 
lastname: We found an element! 
	job: We found an element! 
	gender: We found an element! 
	

The firstname element in the XML document matches all of the first three templates. But, the processor chooses the second template because it has the most specific match. The output is shown below with the output resulting from the firstname match in bold.

Conflicts such as the ones seen in the example above are resolved by assigning priorities to templates. We'll see how to explicitly assign priorities in a moment, but first let's look at the default priorities.

Default Priorities

There are three default levels of priority assigned to patterns in templates.

  1. Patterns that match a class of nodes (e.g., *) in any namespace have a default priority of -0.5.
  2. Patterns that match a class of nodes in a specific namespace (e.g., cw:*) have a default priority of -0.25.
  3. Patterns that match a node name (e.g., firstname) have a default priority of 0.
  4. Patterns that match a node according to its context (e.g., firstname[following-sibling::lastname] and name/firstname) have a default priority of 0.5.

Only the template with the highest priority will be matched. But what happens if a node matches two templates with the same priority. Compare the following two examples.

Code Sample:

TemplatesParametersVariables/Demos/SamePriority.xsl
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" 
				xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text"/>

<xsl:template match="name/firstname">
	<xsl:value-of select="name()"/>: 
		We found a first name in a name element! 
	<xsl:apply-templates/>
</xsl:template>
	
<xsl:template match="firstname[following-sibling::lastname]">
	<xsl:value-of select="name()"/>: 
		We found a first name followed by a last name! 
</xsl:template>

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

Code Sample:

TemplatesParametersVariables/Demos/SamePriority2.xsl
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" 
				xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text"/>
	
<xsl:template match="firstname[following-sibling::lastname]">
	<xsl:value-of select="name()"/>: 
		We found a first name followed by a last name! 
</xsl:template>

<xsl:template match="name/firstname">
	<xsl:value-of select="name()"/>: 
		We found a first name in a name element! 
	<xsl:apply-templates/>
</xsl:template>

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

The only difference between these two XSLTs is the order of the templates. The specification states that it is an error to have two templates in an XSLT with equal precedence. However, it gives the XSLT processor the option of reporting this error or using the last-is-first rule, meaning that the template defined later should be used. In practice, XSLT processors generally use the last-is-first rule rather than report an error.

So, when you transform the SamePriority.xml against SamePriority.xsl, the output reads "firstname: We found a first name followed by a last name!"

When you transform the SamePriority2.xml against SamePriority2.xsl, the output reads "firstname: We found a first name in a name element!"

Assigning Priorities

Priorities can be explicitly assigned to templates using the priority attribute. By assigning a priority higher than .5 (e.g., 1), you ensure that the template in question will take precedence over any template that does not include an explicitly-defined priority. The example below illustrates this.

Code Sample:

TemplatesParametersVariables/Demos/SetPriority.xsl
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" 
				xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text"/>

<xsl:template match="name/firstname" priority="1">
	<xsl:value-of select="name()"/>: 
		We found a first name in a name element! 
	<xsl:apply-templates/>
</xsl:template>
	
<xsl:template match="firstname[following-sibling::lastname]">
	<xsl:value-of select="name()"/>: 
		We found a first name followed by a last name! 
</xsl:template>

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

When you transform the SetPriority.xml against SetPriority.xsl, the output reads "firstname: We found a first name in a name element!", because the template that produces this output has an assigned priority of 1, which is higher than the default priority of the template that immediately follows it in the code.

XSLT Variables

Variables in XSLT are similar to constants in other programming languages; there is no way to change their values once they have been set. They are often used to store complex XPaths or values that will be used repeatedly. We'll illustrate with an example.

Code Sample:

TemplatesParametersVariables/Demos/BeatlesVariables.xml
<?xml version="1.0"?>
<?xml-stylesheet href="BeatlesVariables.xsl" type="text/xsl"?>
<beatles>
	<beatle link="http://www.paulmccartney.com">
		<name>
			<firstname>Paul</firstname>
			<lastname>McCartney</lastname>
		</name>
	</beatle>
	<beatle link="http://www.johnlennon.com">
		<name>
			<firstname>John</firstname>
			<lastname>Lennon</lastname>
		</name>
	</beatle>
	<beatle link="http://www.georgeharrison.com">
		<name>
			<firstname>George</firstname>
			<lastname>Harrison</lastname>
		</name>
	</beatle>
	<beatle link="http://www.ringostarr.com">
		<name>
			<firstname>Ringo</firstname>
			<lastname>Starr</lastname>
		</name>
	</beatle>
</beatles>

Code Sample:

TemplatesParametersVariables/Demos/BeatlesVariables.xsl
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" 
				xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
	<xsl:output method="xml" indent="yes"/>
	<xsl:variable name="lc">abcdefghijklmnopqrstuvwxyz</xsl:variable>
	<xsl:variable name="uc">ABCDEFGHIJKLMNOPQRSTUVWXYZ</xsl:variable>
	<xsl:variable name="Band" 
		select="translate(name(/*[1]),$lc,$uc)"/>
	<xsl:template match="/">
		<html>
			<head>
				<title><xsl:value-of select="$Band"/></title>
			</head>
			<body>
				<h1 align="center"><xsl:value-of select="$Band"/></h1>
				<ul>
					<xsl:for-each select="//beatle">
						<li>
							<xsl:value-of select="name/firstname"/>
							<xsl:text> </xsl:text>
							<xsl:value-of select="name/lastname"/>
						</li>
					</xsl:for-each>
				</ul>
			</body>
		</html>
	</xsl:template>
</xsl:stylesheet>

As shown above, variables are created with the xsl:variable tag. The name of the variable is specified by the name attribute and the value can either be specified in the select attribute or between the open and close xsl:variable tags. The result of transforming TemplatesParametersVariables/Demos/BeatlesVariables.xml against TemplatesParametersVariables/Demos/BeatlesVariables.xsl is shown below.

Code Sample:

TemplatesParametersVariables/Demos/BeatlesVariablesOutput.html
<?xml version="1.0" encoding="UTF-8"?>
<html>
	<head>
		<title>BEATLES</title>
	</head>
	<body>
		<h1 align="center">BEATLES</h1>
		<ul>
			<li>Paul McCartney</li>
			<li>John Lennon</li>
			<li>George Harrison</li>
			<li>Ringo Starr</li>
		</ul>
	</body>
</html>