06. Introducing XSL

The eXtensible Stylesheet Language (XSL) is an XML-based language that can transform an XML document into another XML document, or into an entirely different type of document. Typically the transformation will produce HTML that can be viewd in a web browser.

XSL stylesheets are simply text documents that can be created in any plain text editor, such as Notepad. They contain rules that govern how the content of an XML document should be handled and are traditionally saved with an ".xsl" file extension.

As XSL stylesheets are written in XML they must begin with the standard XML identifier processing instruction, like all other XML documents:

<?xml version="1.0" encoding="UTF-8"?>

The root element of an XSL stylesheet is <xsl:stylesheet> and this must contain a version attribute stating the version number, currently "1.0", and specify the "xsl" namespace prefix using the URL of "http://www.w3.org/1999/XSL/Transform". Additionally the root element can specify one or more namespace prefixes using the URL of the namespaces that are specified in the XML document it is to style - matching the namespaces.

Each XSL stylesheet may optionallyl include a top-level <xsl:output> element defining the format of the output. If included, this should be a child of the <xsl:stylesheet> element and will typically specifiy the method and encoding of the output. This means that the skeleton of a typical XSL stylesheet might look like this:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:c="http://www.auxy.com/xsd">
<xsl:output method="html" encoding="UTF-8"/>
</xsl:stylesheet>

For instance:

<?xml version = "1.0" encoding = "UTF-8" ?>
<?xml-stylesheet type = "text/xsl" href = "to_be_replaced.xsl" ?>
<car:doc
xmlns:xsi = "http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation = "http://www.auxy.com/xsd cars.xsd"
xmlns:car = "http://www.auxy.com/xsd" >
<car:item id = "corvette">
<car:make>Chevrolet</car:make>
<car:model>Corvette</car:model>
<car:ltr>6.0</car:ltr>
<car:cyl>8</car:cyl>
<car:hp>400</car:hp>
<car:price>53000</car:price>
</car:item>
<car:item id = "viper">
<car:make>Dodge</car:make>
<car:model>Viper</car:model>
<car:ltr>8.3</car:ltr>
<car:cyl>10</car:cyl>
<car:hp>510</car:hp>
<car:price>85000</car:price>
</car:item>
<car:item id = "solstice">
<car:make>Pontiac</car:make>
<car:model>Solstice</car:model>
<car:ltr>2.4</car:ltr>
<car:cyl>4</car:cyl>
<car:hp>177</car:hp>
<car:price>22000</car:price>
</car:item>
</car:doc>

Filename: cars.xsd

<?xml version = "1.0" encoding = "UTF-8" ?>
<xsd:schema
xmlns:xsd = "http://www.w3.org/2001/XMLSchema"
targetNamespace = "http://www.auxy.com/xsd"
xmlns:tns = "http://www.auxy.com/xsd" >
<!-- DECLARE ELEMENTS. -->
<!-- Simple types. -->
<xsd:element name = "make" type = "xsd:string" />
<xsd:element name = "model" type = "xsd:string" />
<xsd:element name = "ltr" type = "xsd:decimal" />
<xsd:element name = "cyl" type = "xsd:integer" />
<xsd:element name = "hp" type = "xsd:integer" />
<xsd:element name = "price" type = "xsd:integer" />
<!-- Complex types. -->
<xsd:element name = "doc" type = "tns:docType" />
<xsd:element name = "item" type = "tns:itemType" />
<!-- DEFINE STRUCTURES. -->
<xsd:complexType name = "docType">
<xsd:sequence maxOccurs = "unbounded">
<xsd:element ref = "tns:item" />
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name = "itemType">
<xsd:sequence>
<xsd:element ref = "tns:make" />
<xsd:element ref = "tns:model" />
<xsd:element ref = "tns:ltr" />
<xsd:element ref = "tns:cyl" />
<xsd:element ref = "tns:hp" />
<xsd:element ref = "tns:price" />
</xsd:sequence>
<xsd:attribute name = "id" type = "xsd:string" use = "required" />
</xsd:complexType>
</xsd:schema>

Selecting element values

XSL stylesheet rules are defined as "templates" that specify how the data contained in the XML document should be handled.

Templates are created using the <xsl:template> element, which must have a match attribute to specify how the rule should be applied. The match attribute can associate the rule with a specific XML child element or it can be associated with the entire XML document by specifying the root element.

The <xsl:template> element can encompass text, HTML tags and inline CSS style rules, which are to be included in its output. Additionally data values can be extracted from the elements of the associated XML document using the <xsl:value-of> element. This must have a select attribute to specify the XML element whose value is to be included in the stylesheet's output.

Filename: value-of.xsl

<?xml version = "1.0" encoding = "UTF-8" ?>
<xsl:stylesheet version = "1.0"
xmlns:xsl = "http://www.w3.org/1999/XSL/Transform"
xmlns:c = "http://www.auxy.com/xsd" >
<xsl:output method="html" encoding="UTF-8" indent="yes"/>

<xsl:template match = "c:doc">
<html> <head><title>XSL Output</title> </head> <body>
<ul style = "list-style-type:square;color:red">
<li> <xsl:value-of select = "c:item/c:make" /> </li>
<li> <xsl:value-of select = "c:item/c:model" /> </li>
<li> <xsl:value-of select = "c:item/c:ltr" /> Liters </li>
<li> <xsl:value-of select = "c:item/c:cyl" /> Cylinders</li>
<li> <xsl:value-of select = "c:item/c:hp" /> Horsepower</li>
<li> $ <xsl:value-of select = "c:item/c:price" /> </li>
</ul>
</body> </html>
</xsl:template>
</xsl:stylesheet>

<?xml version = "1.0" encoding = "UTF-8" ?>
<?xml-stylesheet type = "text/xsl" href = "value-of.xsl" ?>
<car:doc
xmlns:xsi = "http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation = "http://www.auxy.com/xsd cars.xsd"
xmlns:car = "http://www.auxy.com/xsd" >
<car:item id = "corvette">
<car:make>Chevrolet</car:make>
<car:model>Corvette</car:model>
<car:ltr>6.0</car:ltr>
<car:cyl>8</car:cyl>
<car:hp>400</car:hp>
<car:price>53000</car:price>
</car:item>
<car:item id = "viper">
<car:make>Dodge</car:make>
<car:model>Viper</car:model>
<car:ltr>8.3</car:ltr>
<car:cyl>10</car:cyl>
<car:hp>510</car:hp>
<car:price>85000</car:price>
</car:item>
<car:item id = "solstice">
<car:make>Pontiac</car:make>
<car:model>Solstice</car:model>
<car:ltr>2.4</car:ltr>
<car:cyl>4</car:cyl>
<car:hp>177</car:hp>
<car:price>22000</car:price>
</car:item>
</car:doc>

Selecting each element

The nature of data stored in an XML document makes it often desirable to have a stylesheet output its contents in tabular form. XSL provides an <xsl:for-each> element that makes this simple.

An <xsl:for-each> element must have a select attribute to specify an XML element to be selected. Where the XML document contains multiple instances of the specified element, each one will be selected in turn. The <xsl:for-each> element can encompass nested <xsl:value-of> elements to output the data values of child elements within each selected parent. This allows the data to be output as a table - where the selection of each parent element by the <xsl:for-each> element begins a new row, and the data selected by each <xsl:value-of> appears in a cell on that row.

Filename: for-each.xsl

<?xml version = "1.0" encoding = "UTF-8" ?>
<xsl:stylesheet version = "1.0"
xmlns:xsl = "http://www.w3.org/1999/XSL/Transform"
xmlns:c = "http://www.auxy.com/xsd" >
<xsl:output method="html" encoding="UTF-8" indent="yes"/>
<xsl:template match = "c:doc">
<html><head><title>XSL Output</title></head><body>
<table style = "width:450px">
<tr style="color:white; background:blue">
<th>Make</th> <th>Model</th> <th>Liters</th> <th>Cyls</th>
<th>HP</th> <th>$</th>
</tr>
<xsl:for-each select = "c:item" >
<tr style= "color:navy;background:aqua;text-align:center" >
<td> <xsl:value-of select = "c:make" /> </td>
<td> <xsl:value-of select = "c:model" /> </td>
<td> <xsl:value-of select = "c:ltr" /> </td>
<td> <xsl:value-of select = "c:cyl" /> </td>
<td> <xsl:value-of select = "c:hp" /> </td>
<td> <xsl:value-of select = "c:price" /> </td>
</tr>
</xsl:for-each>
</table>
</body></html>
</xsl:template>
</xsl:stylesheet>

<?xml version = "1.0" encoding = "UTF-8" ?>
<?xml-stylesheet type = "text/xsl" href = "for-each.xsl" ?>
<car:doc
xmlns:xsi = "http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation = "http://www.auxy.com/xsd cars.xsd"
xmlns:car = "http://www.auxy.com/xsd" >
<car:item id = "corvette">
<car:make>Chevrolet</car:make>
<car:model>Corvette</car:model>
<car:ltr>6.0</car:ltr>
<car:cyl>8</car:cyl>
<car:hp>400</car:hp>
<car:price>53000</car:price>
</car:item>
<car:item id = "viper">
<car:make>Dodge</car:make>
<car:model>Viper</car:model>
<car:ltr>8.3</car:ltr>
<car:cyl>10</car:cyl>
<car:hp>510</car:hp>
<car:price>85000</car:price>
</car:item>
<car:item id = "solstice">
<car:make>Pontiac</car:make>
<car:model>Solstice</car:model>
<car:ltr>2.4</car:ltr>
<car:cyl>4</car:cyl>
<car:hp>177</car:hp>
<car:price>22000</car:price>
</car:item>
</car:doc>

Sorting selected elements

An <xsl:for-each> element selects all instances of the XML element specified by its select attribute in the order in which they appear in the XML document - from the first instance to the last instance. They may, however, be sorted into a different order by adding an <xsl:sort> element between the <xsl:for-each> tags.

The <xsl:sort> element must have a select attribute to specify the element upon which to base the sorting order. Sorting is implemented, by default, in "ascending" order from A-Z alphabetically, and from the lowest number upwards numerically. This can be reversed by adding an order attribute to the <xsl:sort> element, specifying that sorting should be "descending".

Where selected elements contain mixed text and numerical content a data-type attribute can explicitly specify "text" to sort alphabetically, or "number" to sort numerically.

Filename: sort.xsl

<?xml version = "1.0" encoding = "UTF-8" ?>
<xsl:stylesheet version = "1.0"
xmlns:xsl = "http://www.w3.org/1999/XSL/Transform"
xmlns:c = "http://www.auxy.com/xsd" >
<xsl:output method="html" encoding="UTF-8" indent="yes"/>
<xsl:template match = "c:doc">
<html><head><title>XSL Output</title></head><body>
<table style = "width:450px">
<tr style="color:white; background:green">
<th>Make</th> <th>Model</th> <th>Liters</th> <th>Cyls</th>
<th>HP</th> <th>$</th>
</tr>
<xsl:for-each select = "c:item" >
<xsl:sort select = "c:ltr" order = "ascending" data-type = "number" />
<tr style= "color:black;background:lime;text-align:center" >
<td> <xsl:value-of select = "c:make" /> </td>
<td> <xsl:value-of select = "c:model" /> </td>
<td> <xsl:value-of select = "c:ltr" /> </td>
<td> <xsl:value-of select = "c:cyl" /> </td>
<td> <xsl:value-of select = "c:hp" /> </td>
<td> <xsl:value-of select = "c:price" /> </td>
</tr>
</xsl:for-each>
</table>
</body></html>
</xsl:template>
</xsl:stylesheet>

<?xml version = "1.0" encoding = "UTF-8" ?>
<?xml-stylesheet type = "text/xsl" href = "sort.xsl" ?>
<car:doc
xmlns:xsi = "http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation = "http://www.auxy.com/xsd cars.xsd"
xmlns:car = "http://www.auxy.com/xsd" >
<car:item id = "corvette">
<car:make>Chevrolet</car:make>
<car:model>Corvette</car:model>
<car:ltr>6.0</car:ltr>
<car:cyl>8</car:cyl>
<car:hp>400</car:hp>
<car:price>53000</car:price>
</car:item>
<car:item id = "viper">
<car:make>Dodge</car:make>
<car:model>Viper</car:model>
<car:ltr>8.3</car:ltr>
<car:cyl>10</car:cyl>
<car:hp>510</car:hp>
<car:price>85000</car:price>
</car:item>
<car:item id = "solstice">
<car:make>Pontiac</car:make>
<car:model>Solstice</car:model>
<car:ltr>2.4</car:ltr>
<car:cyl>4</car:cyl>
<car:hp>177</car:hp>
<car:price>22000</car:price>
</car:item>
</car:doc>

Selecting elements on condition

An <xsl:for-each> element selects all instancese of the XML element specified by its select attribute then applies its rules to each instance. A conditional test can be added with an <xsl:if> element to determine whether that instance should be included in output - it will only be included if the test evaluates as "true".

The <xsl:if> element must have a test attribute to specify the conditional test expression, which may use any of these operators:

Operator Tests for Example
= Equality

test = "c:make = 'Dodge' "

!= Inequality

test = "c:model != 'Viper' "

& lt; Less than test = "c:hp & lt; 500"
& gt; Greater than test = "c:price & gt; 50000"

For instance:

Filename: if.xsl

<?xml version = "1.0" encoding = "UTF-8" ?>
<xsl:stylesheet version = "1.0"
xmlns:xsl = "http://www.w3.org/1999/XSL/Transform"
xmlns:c = "http://www.auxy.com/xsd" >
<xsl:output method="html" encoding="UTF-8" indent="yes"/>
<xsl:template match = "c:doc">
<html><head><title>XSL Output</title></head><body>
<table style = "width:450px">
<tr style="color:white; background:orange">
<th>Make</th> <th>Model</th> <th>Liters</th> <th>Cyls</th>
<th>HP</th> <th>$</th>
</tr>
<xsl:for-each select = "c:item" >
<xsl:if test = "c:cyl &gt; 4" >
<tr style= "color:red;background:yellow;text-align:center" >
<td> <xsl:value-of select = "c:make" /> </td>
<td> <xsl:value-of select = "c:model" /> </td>
<td> <xsl:value-of select = "c:ltr" /> </td>
<td> <xsl:value-of select = "c:cyl" /> </td>
<td> <xsl:value-of select = "c:hp" /> </td>
<td> <xsl:value-of select = "c:price" /> </td>
</tr>
</xsl:if>
</xsl:for-each>
</table>
</body></html>
</xsl:template>
</xsl:stylesheet>

<?xml version = "1.0" encoding = "UTF-8" ?>
<?xml-stylesheet type = "text/xsl" href = "if.xsl" ?>
<car:doc
xmlns:xsi = "http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation = "http://www.auxy.com/xsd cars.xsd"
xmlns:car = "http://www.auxy.com/xsd" >
<car:item id = "corvette">
<car:make>Chevrolet</car:make>
<car:model>Corvette</car:model>
<car:ltr>6.0</car:ltr>
<car:cyl>8</car:cyl>
<car:hp>400</car:hp>
<car:price>53000</car:price>
</car:item>
<car:item id = "viper">
<car:make>Dodge</car:make>
<car:model>Viper</car:model>
<car:ltr>8.3</car:ltr>
<car:cyl>10</car:cyl>
<car:hp>510</car:hp>
<car:price>85000</car:price>
</car:item>
<car:item id = "solstice">
<car:make>Pontiac</car:make>
<car:model>Solstice</car:model>
<car:ltr>2.4</car:ltr>
<car:cyl>4</car:cyl>
<car:hp>177</car:hp>
<car:price>22000</car:price>
</car:item>
</car:doc>

Choosing alternative elements

An <xsl:for-each> element selects all instances of the XML element specified by its select attribute and <xsl:choose> can provide a choice of rules to be applied according to the result of a conditional test.

Within an <xsl:choose> element the test expression appears in an <xsl:when> element, which has a test attribute to specify a test expression to evaluate - like <xsl:if>. Rules to be included in output when the test evaluates as "true" are contained within the <xsl:when> element. Alternative rules, to be included in output when the test evaluates as "false", are contained within an <xsl:otherwise> element.

Filename: when.xsl

<?xml version = "1.0" encoding = "UTF-8" ?>
<xsl:stylesheet version = "1.0"
xmlns:xsl = "http://www.w3.org/1999/XSL/Transform"
xmlns:c = "http://www.auxy.com/xsd" >
<xsl:output method="html" encoding="UTF-8" indent="yes"/>
<xsl:template match = "c:doc">
<html><head><title>XSL Output</title></head><body>
<table style = "width:450px">
<tr style="color:white; background:red">
<th>Make</th> <th>Model</th> <th>$</th>
</tr>
<xsl:for-each select = "c:item" >
<xsl:choose>
<xsl:when test = "c:price &lt; 60000" >
<tr style= "color:black;background:pink;text-align:center" >
<td> <xsl:value-of select = "c:make" /> </td>
<td> <xsl:value-of select = "c:model" /> </td>
<td> <xsl:value-of select = "c:price" /> </td>
</tr>
</xsl:when>

<xsl:otherwise>
<tr style= "color:yellow;background:blue;font-weight:bold;text-align:center" >
<td> <xsl:value-of select = "c:make" /> </td>
<td> <xsl:value-of select = "c:model" /> </td>
<td> <xsl:value-of select = "c:price" /> </td>
</tr>
</xsl:otherwise>

</xsl:choose>
</xsl:for-each>
</table>
</body></html>
</xsl:template>
</xsl:stylesheet>

<?xml version = "1.0" encoding = "UTF-8" ?>
<?xml-stylesheet type = "text/xsl" href = "when.xsl" ?>
<car:doc
xmlns:xsi = "http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation = "http://www.auxy.com/xsd cars.xsd"
xmlns:car = "http://www.auxy.com/xsd" >
<car:item id = "corvette">
<car:make>Chevrolet</car:make>
<car:model>Corvette</car:model>
<car:ltr>6.0</car:ltr>
<car:cyl>8</car:cyl>
<car:hp>400</car:hp>
<car:price>53000</car:price>
</car:item>
<car:item id = "viper">
<car:make>Dodge</car:make>
<car:model>Viper</car:model>
<car:ltr>8.3</car:ltr>
<car:cyl>10</car:cyl>
<car:hp>510</car:hp>
<car:price>85000</car:price>
</car:item>
<car:item id = "solstice">
<car:make>Pontiac</car:make>
<car:model>Solstice</car:model>
<car:ltr>2.4</car:ltr>
<car:cyl>4</car:cyl>
<car:hp>177</car:hp>
<car:price>22000</car:price>
</car:item>
</car:doc>

Applying multiple templates

A template associates rules with an individual XML element by specifying its tag name to the match attribute of an <xsl:template> XSL element. Each template can then be incorporated into other templates by specifying its tag name to the select attribute of an <xsl:apply-templates> XSL element.

Filename: apply.xsl

<?xml version = "1.0" encoding = "UTF-8" ?>
<xsl:stylesheet version = "1.0"
xmlns:xsl = "http://www.w3.org/1999/XSL/Transform"
xmlns:c = "http://www.auxy.com/xsd" >
<xsl:output method = "html" encoding = "UTF-8" indent = "yes" />
<xsl:template match = "c:doc">
<html><head><title>XSL Output</title></head><body>
<table width = "450px">
<tr style = "color:white;background:black">
<th>Make</th> <th>Model</th> <th>$</th> </tr>
<xsl:apply-templates select = "c:item" />
</table></body></html>
</xsl:template>
<!-- Table row rule. -->
<xsl:template match = "c:item" >
<tr>
<xsl:apply-templates select = "c:make" />
<xsl:apply-templates select = "c:model" />
<xsl:apply-templates select = "c:price" />
</tr>
</xsl:template>
<!-- Table cell rule #1. -->
<xsl:template match = "c:make" >
<td style = "color:white;background:red;text-align:center">
<xsl:value-of select = "." /> </td>
</xsl:template>
<!-- Table cell rule #2. -->
<xsl:template match = "c:model" >
<td style = "color:black;background:lime;text-align:center">
<xsl:value-of select = "." /> </td>
</xsl:template>
<!-- Table cell rule #3. -->
<xsl:template match = "c:price" >
<td style = "color:white;background:blue;text-align:center">
<xsl:value-of select = "." /> </td>
</xsl:template>

</xsl:stylesheet>

<?xml version = "1.0" encoding = "UTF-8" ?>
<?xml-stylesheet type = "text/xsl" href = "apply.xsl" ?>
<car:doc
xmlns:xsi = "http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation = "http://www.auxy.com/xsd cars.xsd"
xmlns:car = "http://www.auxy.com/xsd" >
<car:item id = "corvette">
<car:make>Chevrolet</car:make>
<car:model>Corvette</car:model>
<car:ltr>6.0</car:ltr>
<car:cyl>8</car:cyl>
<car:hp>400</car:hp>
<car:price>53000</car:price>
</car:item>
<car:item id = "viper">
<car:make>Dodge</car:make>
<car:model>Viper</car:model>
<car:ltr>8.3</car:ltr>
<car:cyl>10</car:cyl>
<car:hp>510</car:hp>
<car:price>85000</car:price>
</car:item>
<car:item id = "solstice">
<car:make>Pontiac</car:make>
<car:model>Solstice</car:model>
<car:ltr>2.4</car:ltr>
<car:cyl>4</car:cyl>
<car:hp>177</car:hp>
<car:price>22000</car:price>
</car:item>
</car:doc>

Generating attribute values

An XSL stylesheet can output an HTML element containing an attribute generated by an <xsl:attribute> element, and selected XML data can be assigned to the attribute as its value.

The <xsl:attribute> element has a name attribute, to specify the name of the attribute, and the content between its tags will become its value - an <xsl:value-of> element can be used here to specify XML data as the attribute value. XML attribute data can be referenced by prefixing the attribute name with an @ character.

XSL can use concat function that takes two arguments, separated by a comma, that will be combined into a single string. This can be used to add file extensions to selected XML data. For instance, concat('super','.png') is output as super.png and could be used to output an image of that file name.

Now let's generates image src attribute values, by concatenating id values into image file names:

Filename: attribute.xsl

<?xml version = "1.0" encoding = "UTF-8" ?>
<xsl:stylesheet version = "1.0"
xmlns:xsl = "http://www.w3.org/1999/XSL/Transform"
xmlns:c = "http://www.auxy.com/xsd" >
<xsl:output method = "html" encoding = "UTF-8" />
<xsl:template match = "c:doc">
<html><head><title>XSL Output</title></head><body>
<xsl:apply-templates select = "c:item" />
</body></html>
</xsl:template>
<xsl:template match = "c:item" >
<xsl:value-of select = "c:model" />
<br/>
<img>
<xsl:attribute name = "src">
<xsl:value-of select = "concat( ./@id, '.jpg' ) " />
</xsl:attribute>
</img>

<br/>
</xsl:template>
</xsl:stylesheet>

<?xml version = "1.0" encoding = "UTF-8" ?>
<?xml-stylesheet type = "text/xsl" href = "attribute.xsl" ?>
<car:doc
xmlns:xsi = "http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation = "http://www.auxy.com/xsd cars.xsd"
xmlns:car = "http://www.auxy.com/xsd" >
<car:item id = "corvette">
<car:make>Chevrolet</car:make>
<car:model>Corvette</car:model>
<car:ltr>6.0</car:ltr>
<car:cyl>8</car:cyl>
<car:hp>400</car:hp>
<car:price>53000</car:price>
</car:item>
<car:item id = "viper">
<car:make>Dodge</car:make>
<car:model>Viper</car:model>
<car:ltr>8.3</car:ltr>
<car:cyl>10</car:cyl>
<car:hp>510</car:hp>
<car:price>85000</car:price>
</car:item>
<car:item id = "solstice">
<car:make>Pontiac</car:make>
<car:model>Solstice</car:model>
<car:ltr>2.4</car:ltr>
<car:cyl>4</car:cyl>
<car:hp>177</car:hp>
<car:price>22000</car:price>
</car:item>
</car:doc>