XSLT to HTML Primer

Rescuing XSLT from Niche Status - original article by David Jacobs

http://www.xfront.com/rescuing-xslt.html

 

This tutorial will focus on the generation of HTML documents.

XSLT is a programming language for translating an Input XML Document into another format, say an HTML Output Document.

For each element, you need to determine how the element needs to be translated.

If there are one-to-one mappings or one to zero mappings, this is straightforward. For example, if every occurrence of a <name> element is going to become an HTML header. It is a simple matter to write a matching template to accomplish this.

<xsl:template match="name">

<h1><xsl:apply-templates/></h1>

</xsl:template>

For one-to-many mappings (i.e. when an element’s contents will appear multiple times in the target document with different formatting), keeping track of all the relationships quickly grows in complexity and becomes confusing. For example, if, after writing the previous template, the programmer discovers that the name also needs to be placed in the title the programmer might add the template

<xsl:template match="/">

<title><xsl:value-of select="name"/></title>

</xsl:template>

Notice the use of the <xsl:value-of> function in this template because using <apply-templates select="name"> would have caused a triggering of the previous template adding undesired header tags to my content. Therefors, before adding a translation to an element, the programmer must first be aware of all the existing translations (ugh!). Of course if the programmer became aware of the <title> requirement first, the contents of these templates could have been reversed. One can quickly see how the arbitrary decisions of development and discovery of requirements can lead to a set of templates that are no longer intuitive.

<xsl:value-of> and {}

Simple HTML Result welcome page:

<html>

<head>

<title>Welcome</title>

</head>

<body>

Welcome!

</body>

</html>

XML data document #1 :

<?xml version="1.0"?>

<member level="platinum">

<name>Jeff</name>

<phone type="home">555-1234</phone>

<phone type="work">555-4321</phone>

<favoriteColor>blue</favoriteColor>

</member>

XSLT stylesheet #1a - customize the welcome page to use the member’s name from the XML document.

<?xml version="1.0"?>

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"

version="1.0">

<xsl:template match="/">

<html>

<head>

<title>Welcome</title>

</head>

<body>

Welcome <xsl:value-of select="member/name"/>!

</body>

</html>

</xsl:template>

</xsl:stylesheet>

HTML Result #1a

<html>

<head>

<META http-equiv="Content-Type" content="text/html; charset=UTF-16">

<title>Welcome</title>

</head>

<body>

Welcome Jeff!

</body>

</html>

XSLT stylesheet #1 properties:

well-formed XML document.

HTML used must conform to the XHTML specification (i.e. all tags must be closed and lowercase).

Notice that the XSLT stylesheet HTML is identical to the original HTML except for the introduction of a new tag <xsl:value-of>.

The <xsl:value-of> tag is the key to extracting any piece of information out an XML document.

The <xsl:value-of> tag has a "select" attribute (select="member/name") that provides the path through the XML document to the information we seek.

In this case <member> is the outer most tag and <name> is the tag underneath it. Slash characters ("/") are used to designate parent/child relationships between tags.

If you are used to navigating around a Unix file system this should feel familiar.

XSLT stylesheet #1b - Further customizing HTML Result by making the welcome in the person’s favorite color using the <font> tag with the "bgcolor" attribute.

Since <xsl:value-of> is an XML tag it is not valid to insert it in an HTML attribute value. So another mechanism is needed to insert information from our XML file there.

<?xml version="1.0"?>

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"

version="1.0">

<xsl:template match="/">

<html>

<head>

<title>Welcome</title>

</head>

<body>

<font bgcolor="{member/favoriteColor}">

Welcome <xsl:value-of select="member/name"/>!

</font>

</body>

</html>

</xsl:template>

</xsl:stylesheet>

HTML Result #1b

<html>

<head>

<META http-equiv="Content-Type" content="text/html; charset=UTF-16">

<title>Welcome</title>

</head>

<body>

<font bgcolor="blue">

Welcome Jeff!

</font>

</body>

</html>

Notice the use of the curly brackets ("{}"). When used within an attribute assignment "{path}" has the exact same effect as <xsl:value-of select="path"/> used outside of attribute assignments.

Queries

Not all paths lead to a single node. For example, what if we wanted to put a person’s home phone number on the page?

XML data document #1 :

<?xml version="1.0"?>

<member level="platinum">

<name>Jeff</name>

<phone type="home">555-1234</phone>

<phone type="work">555-4321</phone>

<favoriteColor>blue</favoriteColor>

</member>

Notice that the XML data document #1 contains two phone entries, each of a different type. If we simply used <xsl:value-of select="member/phone"/> both entries would be returned. We obviously need a way to be more specific. Luckily, XSLT allows the full power of XPath to describe the value(s) of interest. XPath allows conditions on any attribute or tag to be placed in square brackets ("[]") which are then used to restrict the values returned.

So to retrieve the home phone number we would use the path "member/phone[@type=’home’]". Notice the "@" symbol in front of "type". The "@" symbol signifies that we are referring to an attribute. So our new HTML template looks like:

 

XSLT stylesheet #1c -

<?xml version="1.0"?>

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"

version="1.0">

<xsl:template match="/">

<html>

<head>

<title>Welcome</title>

</head>

<body>

<font bgcolor="{member/favoriteColor}">

Welcome <xsl:value-of select="member/name"/>!

<br/>

Your home phone number is:

<xsl:value-of select="member/phone[@type=’home’]"/>

</font>

</body

</html

</xsl:template>

</xsl:stylesheet>

HTML Result #1c

<html>

<head>

<META http-equiv="Content-Type" content="text/html; charset=UTF-16">

<title>Welcome</title>

</head>

<body>

<font bgcolor="blue">

Welcome Jeff!

Your home phone number is:

555-1234</font>

</body>

</html>

<xsl:for-each>

What if this <member> entry had numerous phone numbers and we wanted to print them all in the HTML Result .

If we use <xsl:value-of select="member/phone"/> , we cannot format the phone number into a nice list that describes the type of each number.

The <xsl:for-each> tag loops through each of the elements that match a given path.

So to create a table that contains the phone number type in the first column and the phone number in the second column, the following stylesheet could be used.

XSLT stylesheet #1d -

<?xml version="1.0"?>

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"

version="1.0">

<xsl:template match="/">

<html>

<head>

<title>Welcome</title>

</head>

<body>

<font bgcolor="{member/favoriteColor}">

Welcome <xsl:value-of select="member/name"/>!

</font>

<table>

<tr><th>Type</th><th>Number</th></tr>

<xsl:for-each select="member/phone">

<tr>

<td><xsl:value-of select="@type"/></td>

<td><xsl:value-of select="."/></td>

</tr>

</xsl:for-each>

</table>

</body>

</html>

</xsl:template>

</xsl:stylesheet>

<html>

<head>

<META http-equiv="Content-Type" content="text/html; charset=UTF-16">

<title>Welcome</title>

</head>

<body>

<font bgcolor="blue">

Welcome Jeff!

</font>

<table>

<tr><th>Type</th><th>Number</th></tr>

<tr>

<td>home</td>

<td>555-1234</td>

</tr>

<tr>

<td>work</td>

<td>555-4321</td>

</tr>

</table>

</body>

</html>

The above example brings up a number of issues.

First, while in the loop, all <xsl:value-of/> accesses are relative to the current element being iterated over (in this case <phone>).

Notice the use of the period ("."), which like in a Unix file system means the current element. So in this case the period (".") refers to each phone element as the loop iterates.

Also like in a file system you can address a parent element using a double period ("..") and can access any element in the document by starting over at the root element using a slash ("/").

<xsl:if>

XML data document #1 :

<?xml version="1.0"?>

<member level="platinum">

<name>Jeff</name>

<phone type="home">555-1234</phone>

<phone type="work">555-4321</phone>

<favoriteColor>blue</favoriteColor>

</member>

As a last enhancement to our page let’s add a special offer to "platinum" level members.

The <xsl:if> tag allows us to insert content based on a condition of the data in the XML document.

XSLT stylesheet #1e -

<?xml version="1.0"?>

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"

version="1.0">

<xsl:template match="/">

<html>

<head>

<title>Welcome</title>

</head>

<body>

<font bgcolor="{member/favoriteColor}">

Welcome <xsl:value-of select="member/name"/>!

</font>

<xsl:if test="member[@level='platinum']">

Our special offer to platinum members today is something great

</xsl:if>

<table>

<tr><th>Type</th><th>Number</th></tr>

<xsl:for-each select="member/phone">

<tr>

<td><xsl:value-of select="@type"/></td>

<td><xsl:value-of select="."/></td>

</tr>

</xsl:for-each>

</table>

</body>

</html>

</xsl:template>

</xsl:stylesheet>

Within the "test" attribute the full array of Boolean and relative operators are available. The only caveat is that since this is an XML document less than and greater than ("<", ">") signs must be escaped as "&lt;" and "&gt;".

<xsl:choose>

The <xsl:if> tag has no "else" statement. Luckily XSLT has <xsl:choose> which works like a switch/case statement.

XSLT stylesheet #1f -

<?xml version="1.0"?>

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"

version="1.0">

<xsl:template match="/">

<html>

<head>

<title>Welcome</title>

</head>

<body>

<font bgcolor="{member/favoriteColor}">

Welcome <xsl:value-of select="member/name"/>!

</font>
<xsl:choose>
<xsl:when test="member[@level='platinum']">
Our special offer to platinum members today is something great
</xsl:when>
<xsl:when test="member[@level='aluminum']">
Our special offer to aluminum members today is something great
</xsl:when>
<xsl:otherwise>
Become a platinum member today!
</xsl:otherwise>
</xsl:choose>

<table>

<tr><th>Type</th><th>Number</th></tr>

<xsl:for-each select="member/phone">

<tr>

<td><xsl:value-of select="@type"/></td>

<td><xsl:value-of select="."/></td>

</tr>

</xsl:for-each>

</table>

</body>

</html>

</xsl:template>

</xsl:stylesheet>

The "test" attribute has the same capabilities/ constraints as the "test" attribute in the <xsl:if> tag. Multiple <xsl:when> blocks are allowed. As soon as one "when test" is mached, it will not evaluate any further "xsl:when tests" in the <xsl:choose> block.

XML data document #2 :

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

<ups_CFSStuff_GetBusinessUnitsAndEmployeesselect>

<Table>

<CODE_LIST_ID>98</CODE_LIST_ID>

<DESCRIPTION>CF Commercial Finance</DESCRIPTION>

<Table1>

<CODE_LIST_ID>98</CODE_LIST_ID>

<Employee>Frank Blow</Employee>

<Access>Writeable</Access>

</Table1>

<Table1>

<CODE_LIST_ID>98</CODE_LIST_ID>

<Employee>Joe Blow</Employee>

<Access>Writeable</Access>

</Table1>

</Table>

<Table>

<CODE_LIST_ID>98</CODE_LIST_ID>

<DESCRIPTION>2nd Unit</DESCRIPTION>

<Table1>

<CODE_LIST_ID>98</CODE_LIST_ID>

<Employee>Frank Blow</Employee>

<Access>Writeable</Access>

</Table1>

<Table1>

<CODE_LIST_ID>98</CODE_LIST_ID>

<Employee>Joe Blow</Employee>

<Access>Writeable</Access>

</Table1>

</Table>

</ups_CFSStuff_GetBusinessUnitsAndEmployeesselect>

XSLT stylesheet #2a

<?xml version="1.0"?>

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"

version="1.0">

<xsl:template match="/">

<html>

<head>

<title>Business Units with Employees</title>

</head>

<body style="FONT-FAMILY: Verdana">

Business Units with Employees

<table cellSpacing="0" cellPadding="0" style="BORDER-RIGHT: medium none; BORDER-TOP: medium none; FONT-SIZE: xx-small; BORDER-LEFT: medium none; BORDER-BOTTOM: medium none">

<tr></tr>

<xsl:for-each select="ups_CFSStuff_GetBusinessUnitsAndEmployeesselect/Table">

<tr>

<td style="FONT-SIZE: xx-small; COLOR: blue"><xsl:value-of select="DESCRIPTION"/></td>

</tr>

<xsl:for-each select="Table1">

<tr>

<td> . <xsl:value-of select="Employee"/></td>

<td> - <xsl:value-of select="Access"/></td>

</tr>

</xsl:for-each>

</xsl:for-each>

</table>

</body>

</html>

</xsl:template>

</xsl:stylesheet>

Result:

Business Units with Employees

 

CF Commercial Finance

. Frank Blow

- Writeable

. Joe Blow

- Writeable

2nd Unit

 

. Frank Blow

- Writeable

. Joe Blow

- Writeable

 

 

 

 

Conclusion

With just a few commands (there are many more in XSLT) we can meet many HTML output needs.