An Extensible Stylesheet Language (XSL) Tutorial
This tutorial covers the basics of XSL. Before reading this
tutorial you should already be familiar with XML. You may want to read my XML
tutorial. Click the above link to do so.
XSL stands for Extensible
Stylesheet Language.
XSL is similar to Cascading Style Sheet (CSS)
language in that it lets you format the display of XML data. This, however, is
were the similarities stop. To view an XML file with and without CSS
click here.
With CSS, the structure of the XML data must be identical to the structure
of its display. CSS associates formatting properties with XML tags and is said
to "decorate the XML tree". Thus, forethought must be used when
designing XML data so you can display it properly. This violates XML's goal of
separation of data.
XSL lets you transform the XML tree
into a new tree without changing the XML source data. Then, the XML can be
displayed differently just by switching style sheets.
XSL standards are defined by the
World Wide Web Consortium (W3C). The W3C site provides a comprehensive
reference concerning XLS.
However, discussions contained herein focus on Microsoft's implementation of
XSL. All samples require their Internet Explorer, version 5.0 or later, browser
which includes their Msxml parser. All references to the Msxml parser, assume
Msxml V2.5 or later. For more information or to download Microsoft's XML
products, visit their site.
XSL as a Programming Language
|
Since XSL is a language, it enables you to use conditional formatting and
looping constructs, apply selection criteria, sort and filter the XML data, and
transform the XML tree structure. Unlike CSS, you can also describe irregular,
non-repetative data.
Creating and Filling an HTML Template
|
With XSL you can combine XML data with an HTML template for display purposes.
The following XML data is repetitive -- the stock element is repeated
several times with the same subelements.
XML Sample 1
<?xml version="1.0"?>
<?xml-stylesheet type="text/xsl" href="stock.xsl"?>
<portfolio xmlns:dt="urn:schemas-microsoft-com:datatypes">
<stock exchange="nyse">
<name>zacx corp</name>
<symbol>ZCXM</symbol>
<price dt:dt="number">28.875</price>
</stock>
<stock exchange="nasdaq">
<name>zaffymat inc</name>
<symbol>ZFFX</symbol>
<price dt:dt="number">92.250</price>
</stock>
<stock exchange="nasdaq">
<name>zysmergy inc</name>
<symbol>ZYSZ</symbol>
<price dt:dt="number">20.313</price>
</stock>
</portfolio>
|
Here is a complete XSL style sheet which uses an HTML template to create a
separate row for each stock.
XSL Sample 1
<?xml version='1.0'?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/TR/WD-xsl">
<xsl:template match="/">
<HTML>
<BODY>
<TABLE border="2">
<TR>
<TD>Symbol</TD>
<TD>Name</TD>
<TD>Price</TD>
</TR>
<xsl:for-each select="portfolio/stock">
<TR>
<TD><xsl:value-of select="symbol"/></TD>
<TD><xsl:value-of select="name"/></TD>
<TD><xsl:value-of select="price"/></TD>
</TR>
</xsl:for-each>
</TABLE>
</BODY>
</HTML>
</xsl:template>
</xsl:stylesheet>
|
The <xsl:for-each> element
locates a set of elements in the XML data ("stock" elements inside the
"portfolio" element) and repeats a portion of the template for each one. This
sample contains three stock elements so three rows will be created in the
table.
The Select attribute tells how to find a set of
elements. The syntax for this attribute is called an XSL
pattern. Patterns work like navigating a file system tree where a
slash (/) selects subdirectories relative to the current directory.
In an XSL style sheet, navigation starts at the current node and drills down
into the XML data hierarchy, selecting all nodes matching the pattern. Here,
the pattern portfolio/stock starts at the root and drills down through
the "portfolio" element to select the three "stock" children.
XSL patterns can be complex and are described in more detail later.
Within the <xsl:for-each> element, you can further drill down to
select children of each "stock" element. The <xsl:value-of>
element selects a specific child then inserts that child's text into the
template. The patterns inside the <xsl:value-of> element's select
attribute do not start at the root. They are relative to the element selected
in the <xsl:for-each> element.
Because XSL style sheets are XML files, they begin with an XML declaration.
Also, both they and the HTML template must be well
formed.
The <xsl:stylesheet> element denotes this is
a style sheet file and specifies an XSL namespace URL. The <xsl:template
match="/"> statement indicates the template corresponds to the
root (/) of the XML source document.
In the XML file, a processing instruction was added
so the parser knows to use the XSL style sheet.
Try it! View
the XSL style sheet
View the formatted XML file.
To access an attribute in the XML source precede its name with the "@" symbol.
As shown below, the value of the "exchange" attribute on stock elements is
inserted it into the output.
Attributes can be added to an element when the data is displayed using the
<xsl:attribute> element. The attribute's value is generated
from the XML source data.
<xsl:for-each select="portfolio/stock">
<TR>
<xsl:attribute name="title"><xsl:value-of
select="symbol"/>
is listed on the <xsl:value-of
select="@exchange"/>
stock exchange.</xsl:attribute>
<TD><xsl:value-of
select="symbol"/></TD>
<TD><xsl:value-of
select="name"/></TD>
<TD><xsl:value-of
select="price"/></TD>
</TR>
</xsl:for-each>
|
The name attribute specifies the name of the attribute that will be added
to the output. Its value is determined by evaluating its tag.
In this example, a title attribute is added to the <TR> element to
display a ToolTip. The ToolTip's text is created by mixing text with element
and attribute values from the source XML document.
Attributes can be added to elements that already have attributes specified on
them in the XML source. However, you can't add an attribute to an element that
already has an attribute with the same name. Also, attributes added with
<xsl:attribute> must appear before children are added to the element.
The <xsl:for-each> element displays repetitive data in the order it
appears in the XML document. The order-by attribute
lets you to sort the data within a specified node, price
in the this case.
<xsl:for-each select="portfolio/stock" order-by="price">
|
A minus sign (-) preceding a sort key causes the sort to occur in descending
order. A plus sign (+), which is implied, can optionally be used to explicitly
indicate an ascending sort.
You can sort by multiple keys. Just separate them with semicolons. If multiple
stocks have the same price, they will then be sorted by name. Notice
that the price data is first converted to a numeric data type to insure proper
sorting.
order-by="- number(PRICE); NAME"
|
Conditional logic in XSL lets you display XML data in your HTML based on certain
conditions being met in the XML source. Conditional templates are defined using
the <xsl:if> and <xsl:choose>
elements.
If Logic
The XML data in sample 1 above has an "exchange" attribute associated with the
"stock" element. Suppose you want to output an asterisk "*" when the exchange
is "nasdaq". This can easily be done using the <xsl:if>
element after the select="symbol" line of XSL Sample 1.
<xsl:value-of select="symbol"/>
<xsl:if test="@exchange[.='nasdaq']">*</xsl:if>
|
Instead of displaying text, an asterisk in this case, we could have displayed
elements and attributes as well.
The test attribute uses a pattern. If the query associated with
the pattern selects one or more nodes, the <xsl:if> template is inserted.
The above query looks to see if the stock element has an "exchange" attribute.
Then it checks if the exchange attribute's value equals "nasdaq".
Try it!
View the XSL style sheet
View the resulting XML file
Choose Logic
The Choose element functions like Visual Basic's Select
Case statement. It allows you to specify a series of conditions. When
one is satisfied, the corresponding HTML template is output.
<TR>
<xsl:attribute name="STYLE">color:
<xsl:choose>
<xsl:when test="price[. $le$
25]">green</xsl:when>
<xsl:when test="price[. $le$
50]">blue</xsl:when>
<xsl:otherwise>red</xsl:otherwise>
</xsl:choose>
</xsl:attribute>
<TD>
...
|
A Data Island
is an XML document that exists within an HTML page. Since XSL is an XML grammar
itself, data islands allow you to easily load style sheets.
This example uses two data islands. One to load the XML data and one to load the
XSL style sheet. The transformNode method is used
on the data islands to create the HTML to be inserted into the page.
<HTML>
<HEAD>
<title>Simple demo of Microsoft XSL
Processor</title>
</HEAD>
<XML id="source" src="menuxml.xml"></XML>
<XML id="style" src="menuxsl.xsl"></XML>
<SCRIPT FOR="window" EVENT="onload">
xslTarget.innerHTML =
source.transformNode(style.XMLDocument);
</SCRIPT>
<BODY>
<P STYLE="font-size:10pt; font-family:Verdana;
color:gray">
<B>This demo shows the use of data
islands for loading XML source and
XSL style sheets and inserting the
transformed result into the Web page.</B>
</P>
<DIV id="xslTarget"></DIV>
</BODY>
</HTML>
|
Here's what happens. When the page is completely loaded, including the data
islands, its onload event fires and calls the transformNode method
on the XML source data.
TransformNode takes the DOM node representing the XSL style sheet as a
parameter. The result is XML text. Since the menuxsl.xsl style sheet produces
well-formed HTML, its output can be fed into the xslTarget DIV element
for display using the innerHTML property.
Note that the XMLDocument property is used on the "style" data island.
Without this, the ID of the data island is thought to be an HTML element, in
this case the <XML> element.
We could have just used XMLDocument instead of style.XMLDocument. However,
specifying the full name makes it clear that we are operating on the contents
of the data island and not the <XML> element representing the data
island.
What happens if nothing happens?
Errors can occur during the parsing of the XML and XSL documents and during the
transformation. It is possible to trap and display these errors. This is
explained in detail in the XML Software Development Kit available on
their site.
XSL Patterns provide a query language for
identifying nodes in an XML document. Nodes can be identified by their type,
name, value, and relationship to other nodes in the document. A pattern
describes a path through the XML hierarchy with a slash-separated list
of child element names.
The following query finds 'author' elements with a 'period' attribute whose
value is 'classical' and that are contained in the 'authors' element at the
document root.
"/authors/author[@period='classical']".
|
XML documents represent a hierarchy of nodes, similar to the hierarchy of
folders and files in a file system. Here are some similarities between the two.
File System
|
XSL Patterns
|
Hierarchy of folders and files
|
Hierarchy of elements and other nodes in an XML document
|
Files at each level have unique names
|
Element names at each level might not be unique. XSL Patterns identify all the
matching elements
|
URLs are evaluated relative to the "current folder"
|
XSL Patterns are evaluated relative to a particular node called the
context for the query.
|
Let's use this XML data and illustrate some of the basic queries using XSL
Patterns.
<authors>
<author>
<name>Victor Hugo</name>
<nationality>French</nationality>
</author>
<author period="classical">
<name>Sophocles</name>
<nationality>Greek</nationality>
</author>
<author>
<name>Leo Tolstoy</name>
<nationality>Russian</nationality>
</author>
<author>
<name>Alexander Pushkin</name>
<nationality>Russian</nationality>
</author>
<author period="classical">
<name>Plato</name>
<nationality>Greek</nationality>
</author>
</authors>
|
XSL Patterns can use any node in the XML document as the
context (starting point) for a query. In an XSL style sheet, the
context for a query is the source node being currently processed by an <xsl:template>
or <xsl:for-each> element.
The following pattern starts from the XML document's root and traverses down the
hierarchy to the "name" elements. It identifies all elements that match
the path.
Patterns can also use wildcards. Here, we can find
any name element regardless of whether it is a child of the author
element. We can also find both name and nationality elements
under author elements.
authors/*/name
authors/author/*
|
We can specify branches on the path by using square
brackets. The branch on the author element indicates only author elements
with nationality children should be considered. In the second statement,
only the names of Russian authors are returned.
authors/author[nationality]/name
authors/author[nationality='Russian']/name
|
To use attributes in a query, precede their name
with "@". The attribute can be tested as a branch
off the main path, or the query can identify attribute nodes.
This first example returns authors from the classical period. The second
returns just the two period attributes themselves.
authors/author[@period="classical"]
authors/author/@period
|
The above samples are only the tip of the iceberg when it comes to patterns. The
best reference on patterns is the XML Software Development Kit available from
Microsoft.
|