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
xsl:apply-templates
and xsl:call-template.
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.
<?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.
<?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.
In this exercise, you will practice using xsl:apply-templates.
<?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>
<?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>
The xsl:call-template element is used to call a named template. The following example illustrates how it is used.
<?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:
The xsl:call-template
tag also takes the name
attribute, which simply matches the name of the template started above:
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.
XSLT templates can accept parameters, which can be passed from xsl:apply-templates and xsl:call-template. The following code illustrates this.
<?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:
The nested xsl:with-param
element is used to pass a value for Title to the StartHTML template, which looks like this:
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.
In this exercise, you will practice passing parameters to named templates.
<?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>
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.
<?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>
<?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 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.
<?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.
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.
<?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>
<?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.
Here is how the processor steps through this transformation.
xsl:apply-templates
tag with mode
set to "links" and continues to look through the source XML document's nodes starting from the root.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.beatle
nodes in the XML document.xsl:apply-templates
tag with mode
set to "body" and continues to look through the source XML document's nodes starting from the root.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.beatle
nodes in the XML document.Take a look at the following code samples:
<?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>
<?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>
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.
There are three default levels of priority assigned to patterns in templates.
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.
<?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>
<?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!"
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.
<?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.
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.
<?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>
<?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.
<?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>