Many such languages exist, and some have been around for many years (e.g., awk). The one we will consider here is XSL.
The original XSL proposal was submitted to the W3C in August of 1997. The first working draft was published about a year later. In early 1999 it became apparent that it would be better to think of XSL as two distinct components, XSL Transformations (XSLT) and XSL Formatting Objects (XSL-FO). XSL-FO is similar in spirit to other types of stylesheets (including CSS and DSSSL). XSLT, on the other hand, is a declarative language for selecting and processing the nodes in a document. Since this is a programming course, our concern is with XSLT.
In November of 1999, XSLT (and the closely related XPath, which is used to identify the parts of a document) became W3C recommendations.
The rules in an XSLT program are described using XML. All XSL tags
are in the xsl
namespace. Hence, the are all prefixed
by xsl:
.
XSLT rules can appear in any order. It is, by design, free of side-effects.
An XSLT program can transform an XML (input) document into output of any kind. On the "client side" the most common output format is HTML. Some client-side applications transform XML documents into other XML documents or into ASCII text.
<?xml version="1.0" ?> <?xml-stylesheet type="text/xsl" href="simple.xsl" ?> <text>I Love CS685!</text>
What we would like to do is load this document into a browser,
process it, and display the contents of the text
tag in the browser. Obviously, we could write a procedural
script to accomplish this. However, let's instead think about a
set of rules that would allow us to accomplish the same thing.
Rather than worry about formatting details, we will focus on transforming an XML document into an HTML document (with or without an associated CSS).
First, we need a rule for processing the root element. When the root element is encountered, we want to build the skeleton of the HTML document. This can be accomplished in XSLT as follows:
<xsl:template match="/"> <HTML> <BODY> <xsl:apply-templates /> </BODY> </HTML> </xsl:template>
This rule says to look for an element that matches "/" (the root element).
If one is encountered, insert opening HTML
and
BODY
tags,
look for other matches (and process them), and insert closing
BODY
and HTML
tags.
Second, we need a rule for processing text
elements.
When a text
elements is encountered, we want to
insert a P
element into the HTML skeleton, and insert
the contents of the text
element in the P
.
This can be accomplished in XSLT as follows:
<xsl:template match="text"> <P> <xsl:value-of select="." /> </P> </xsl:template>
This rule says to look for an element that matches "text".
When one is encountered, insert the P
element and the
contents of the text
note.
Putting it all together we have the following:
<?xml version="1.0" ?> <xsl:stylesheet xmlns:xsl="http://www.w3.org/TR/WD-xsl" result-ns="html"> <xsl:template match="/"> <HTML> <BODY> <xsl:apply-templates /> </BODY> </HTML> </xsl:template> <xsl:template match="text"> <P> <xsl:value-of select="." /> </P> </xsl:template> </xsl:stylesheet>
Note that this XSLT program is, itself, an XML document.
xsl:stylesheet
element. It has several attributes, including:
xmlns |
The namespace to use for XSL.
|
result-ns |
The namespace to use for output. When producing HTML output this should
be set to "html".
|
language |
The language that will be used for procedural scripts (if any).
|
indent-result |
When set to "yes" this causes whitespace to be preserved on output.
The default is "no".
|
xsl:template
element defines a template (or rule)
for producing output. It has the following attributes:
match |
The pattern that determines which nodes will be processed
by this template.
|
xsl:apply-templates
element indicates that the
processor should search for and apply matching templates.
It has the following attributes:
select |
The pattern that determines which nodes will be processed.
If this attribute is omitted, all nodes will be processed.
|
xsl:value-of
element returns an XML node.
It has the following attributes:
select |
The pattern that determines which nodes will be processed.
If this attribute is omitted, the current node will be returned.
If more than one node is matched, only the first will be returned.
|
<?xml version="1.0" ?> <xsl:stylesheet xmlns:xsl="http://www.w3.org/TR/WD-xsl" result-ns="html"> <!-- reference.xsl v1.0 --> <!-- root element --> <xsl:template match="/"> <HTML> <BODY> <xsl:apply-templates /> </BODY> </HTML> </xsl:template> <!-- author --> <xsl:template match="author"> <xsl:apply-templates select="firstname"/> <xsl:apply-templates select="lastname"/>, </xsl:template> <!-- bibliography --> <xsl:template match="bibliography"> <xsl:apply-templates /> </xsl:template> <!-- book --> <!-- Note the use of the built-in text() function --> <!-- that prevents the children from being selected --> <xsl:template match="book"> <EM><xsl:value-of select="text()" /></EM>, <xsl:apply-templates select="editor"/> <xsl:apply-templates select="publisher"/> <xsl:apply-templates select="pages"/>. </xsl:template> <!-- editor --> <xsl:template match="editor"> <xsl:apply-templates select="firstname"/> <xsl:apply-templates select="lastname"/>, </xsl:template> <!-- firstname --> <xsl:template match="firstname"> <xsl:value-of select="." /> </xsl:template> <!-- journal --> <!-- Note the use of the built-in text() function --> <!-- that prevents the children from being selected --> <xsl:template match="journal"> <EM><xsl:value-of select="text()" /></EM>, <xsl:apply-templates select="volume"/> <xsl:apply-templates select="pages"/>. </xsl:template> <!-- lastname --> <xsl:template match="lastname"> <xsl:value-of select="." /> </xsl:template> <!-- pages --> <xsl:template match="pages"> pp. <xsl:value-of select="." /> </xsl:template> <!-- publisher --> <xsl:template match="publisher"> <xsl:value-of select="." />, </xsl:template> <!-- reference --> <xsl:template match="reference"> <P> <xsl:apply-templates select="author"/> <xsl:apply-templates select="year"/> <xsl:apply-templates select="title"/> <xsl:apply-templates select="journal"/> <xsl:apply-templates select="book"/> </P> </xsl:template> <!-- title --> <xsl:template match="title"> "<xsl:value-of select="." />", </xsl:template> <!-- volume --> <xsl:template match="volume"> Vol. <xsl:value-of select="." />, </xsl:template> <!-- year --> <xsl:template match="year"> (<xsl:value-of select="." />) </xsl:template> </xsl:stylesheet>
Path expressions use the following operators:
/ |
The child operator. It selects elements that are
children of the specified node. For example,
reference/year refers to the year
element that is a child of the reference element.
|
. |
The current node operator. For example, ./year
refers to the year element that is a child of the
current element.
|
@ |
The attribute operator. For example, train/@number
refers to the number attribute of the train
element.
|
* |
The wildcard operator. For example, timetable/*
refers to all children of the timetable
element.
|
[ ] |
The index operator. For example, stop/time[0]
refers to the first time element that is a child of
the stop element. As another example,
stop/time[end()] refers to the last time
element that is a child of the stop element.
|
<?xml version="1.0"?> <xsl:stylesheet xmlns:xsl="http://www.w3.org/TR/WD-xsl" result-ns="html" language="JScript"> <!-- Root --> <xsl:template match="/"> <HTML> <HEAD> </HEAD> <BODY BGCOLOR="#FFFFFF"> <xsl:apply-templates select="timetable"/> </BODY> </HTML> </xsl:template> <!-- Station --> <xsl:template match="station"> <B> <xsl:value-of select="text()" /> </B> </xsl:template> <!-- Stop --> <xsl:template match="stop"> <TD> <xsl:apply-templates select="station" /> </TD> </xsl:template> <!-- Time --> <xsl:template match="time"> <TD> <xsl:value-of select="text()" /> </TD> </xsl:template> <!-- Timetable --> <!-- Note the use of the attribute operator --> <xsl:template match="timetable"> <H2> <xsl:value-of select="@title"/> </H2> <H3> <xsl:value-of select="@subtitle"/> </H3> <TABLE> <xsl:apply-templates match="train"/> </TABLE> </xsl:template> <!-- Train --> <!-- Note the use of the attribute and child operators --> <xsl:template match="train"> <TR> <TD><xsl:value-of select="@number" /></TD> <TD><xsl:value-of select="@normaldays" /></TD> <xsl:apply-templates select="stop/time" /> </TR> </xsl:template> <!-- Note the use of the index, attribute, and child operators --> <xsl:template match="train[0]"> <TR> <TD><B>Train Number</B></TD> <TD><B>Normal Days</B></TD> <xsl:apply-templates select="stop" /> </TR> <TR> <TD><xsl:value-of select="@number" /></TD> <TD><xsl:value-of select="@normaldays" /></TD> <xsl:apply-templates select="stop/time" /> </TR> </xsl:template> </xsl:stylesheet>
An XSLT filter has the following syntax:
where operator denotes a filter operator and pattern denotes a filter pattern.
The set of operators includes:
= |
The equal value operator. For example, time[@status = 'Ar']
refers to a time element with a status
attribute equal to "Ar".
|
> |
The greater than value operator.
|
< |
The less than value operator.
|
>= |
The greater than or equal to value operator.
|
<= |
The less than or equal to value operator.
|
!= |
The not equal to value operator.
|
|
|
xsl:if
and xsl:choose
.
xsl:if
element has the following attributes:
match |
The pattern that determines the value of the test condition.
|
test |
Evaluate to true if the given element exists
|
For example, the following sippet:
<xsl:if test="pages"> ,<xsl:apply-templates select="pages"/> </xsl:if>
will output a comma and the output of the pages
rule only if there is a pages
element.
xsl:choose
element contains one or more
xsl:when
elements and at most one
csl:otherwise
element, each of which behaves like
an xsl:if
element.
<?xml version="1.0" ?> <xsl:stylesheet xmlns:xsl="http://www.w3.org/TR/WD-xsl" result-ns="html"> <!-- reference.xsl v2.0 --> <!-- root element --> <xsl:template match="/"> <HTML> <BODY> <xsl:apply-templates /> </BODY> </HTML> </xsl:template> <!-- author --> <!-- v2.0 Fixes first author and last author --> <xsl:template match="author"> <xsl:choose> <xsl:when match="author[0]"> <xsl:apply-templates select="lastname"/>, <xsl:apply-templates select="firstname"/> </xsl:when> <xsl:when match="author[end()]"> and <xsl:apply-templates select="firstname"/> <xsl:apply-templates select="lastname"/> </xsl:when> <xsl:otherwise> , <xsl:apply-templates select="firstname"/> <xsl:apply-templates select="lastname"/> </xsl:otherwise> </xsl:choose> </xsl:template> <!-- bibliography --> <xsl:template match="bibliography"> <xsl:apply-templates /> </xsl:template> <!-- book --> <!-- Note the use of the built-in text() function --> <!-- that prevents the children from being selected --> <xsl:template match="book"> <EM><xsl:value-of select="text()" /></EM> , Edited by <xsl:apply-templates select="editor"/> <xsl:apply-templates select="publisher"/> <xsl:apply-templates select="pages"/>. </xsl:template> <!-- editor --> <!-- v2.0 Fixes last editor and last editor --> <xsl:template match="editor"> <xsl:choose> <xsl:when match="editor[0]"> <xsl:apply-templates select="firstname"/> <xsl:apply-templates select="lastname"/> </xsl:when> <xsl:when match="editor[end()]"> and <xsl:apply-templates select="firstname"/> <xsl:apply-templates select="lastname"/> </xsl:when> <xsl:otherwise> , <xsl:apply-templates select="firstname"/> <xsl:apply-templates select="lastname"/> </xsl:otherwise> </xsl:choose> </xsl:template> <!-- firstname --> <xsl:template match="firstname"> <xsl:value-of select="." /> </xsl:template> <!-- journal --> <!-- Note the use of the built-in text() function --> <!-- that prevents the children from being selected --> <!-- --> <!-- v2.0 fixed problem with missing elements --> <xsl:template match="journal"> <EM><xsl:value-of select="text()" /></EM> <xsl:if test="volume"> ,<xsl:apply-templates select="volume"/> </xsl:if> <xsl:if test="pages"> ,<xsl:apply-templates select="pages"/> </xsl:if> . </xsl:template> <!-- lastname --> <xsl:template match="lastname"> <xsl:value-of select="." /> </xsl:template> <!-- pages --> <xsl:template match="pages"> pp. <xsl:value-of select="." /> </xsl:template> <!-- publisher --> <xsl:template match="publisher"> <xsl:value-of select="." />, </xsl:template> <!-- reference --> <xsl:template match="reference"> <P> <xsl:apply-templates select="author"/> <xsl:apply-templates select="year"/> <xsl:apply-templates select="title"/> <xsl:apply-templates select="journal"/> <xsl:apply-templates select="book"/> </P> </xsl:template> <!-- title --> <xsl:template match="title"> "<xsl:value-of select="." />", </xsl:template> <!-- volume --> <xsl:template match="volume"> Vol. <xsl:value-of select="." /> </xsl:template> <!-- year --> <xsl:template match="year"> (<xsl:value-of select="." />) </xsl:template> </xsl:stylesheet>
xsl:script
and xsl:eval
elements.
The xsl:script
element is comparable
to the SCRIPT
element in HTML. That is, it contains
the source code. The xsl:eval
element, on the other hand,
is used to execute a function.
<?xml version="1.0" ?> <xsl:stylesheet xmlns:xsl="http://www.w3.org/TR/WD-xsl" result-ns="html"> <!-- manifest.xsl --> <!-- root element --> <xsl:template match="/"> <HTML> <BODY> <xsl:apply-templates /> </BODY> </HTML> </xsl:template> <!-- box --> <xsl:template match="box"> <TR> <TD> <xsl:apply-templates select="ship-to" /> </TD> <TD> <xsl:apply-templates select="description" /> </TD> <TD> <xsl:apply-templates select="weight" /> </TD> </TR> <xsl:if match="box[end()]"> <TR> <TD> </TD> <TD> </TD> <TD> <STRONG> <xsl:eval>totalWeight.pounds</xsl:eval>lbs <xsl:eval>totalWeight.ounces</xsl:eval>oz </STRONG> </TD> </TR> </xsl:if> </xsl:template> <!-- description --> <xsl:template match="description"> <EM><xsl:value-of select="text()" /></EM> </xsl:template> <!-- manifest --> <xsl:template match="manifest"> <TABLE> <xsl:apply-templates select="box" /> </TABLE> </xsl:template> <!-- ship-to --> <xsl:template match="ship-to"> <xsl:value-of select="text()" /> </xsl:template> <!-- weight --> <xsl:template match="weight"> <xsl:value-of select="@pounds" />lbs <xsl:value-of select="@ounces" />oz <xsl:eval> var lbs, oz, w; lbs = parseInt(this.attributes.getNamedItem("pounds").nodeValue); oz = parseInt(this.attributes.getNamedItem("ounces").nodeValue); w = new Weight(lbs,oz); totalWeight.increase(w); </xsl:eval> </xsl:template> <!-- Scripts --> <xsl:script language="JavaScript"> <![CDATA[ var totalWeight = new Weight(0,0); /** * A Weight class * * Author: CS685 * Department of Computer Science * James Madison University * * Date: 26 August 2000 */ /** * Construct a new Weight * * Arguments: lbs - The number of pounds * oz - The number of ounces */ function Weight(lbs, oz) { if ((lbs < 0) || (oz < 0)) { this.pounds = 0; this.ounces = 0; } else { this.pounds = lbs; this.ounces = oz; this.pounds += Math.floor(this.ounces/16); this.ounces = this.ounces % 16; } } /** * Increase this Weight * * Arguments: inc - The amount of the increase (which is a Weight) * * Side Effects: Increases this Weight */ function _increase(inc) { this.pounds += inc.pounds; this.ounces += inc.ounces; this.pounds += Math.floor(this.ounces/16); this.ounces = this.ounces % 16; } // Make _increase a method of the prototype // Weight.prototype.increase = _increase; ]]> </xsl:script> </xsl:stylesheet>