XSL Templates by Example

by James Tauber

Version:1999-03-03

NOTE: THIS DOCUMENT IS NOW OUT OF DATE. I WILL UPDATE IT AT SOME STAGE TO REFLECT THE LATEST XSLT DRAFT.

This document briefly outlines some of the "real-life" templates I use for generating XMLSOFTWARE. The site is authored in XML and transformed to HTML using XT and a series of stylesheets (some of which are generated via other stylesheets). Here I have taken extracts that demonstrate the different things XSL can do, have ordered them in increasing complexity and added some commentary on what each does.

The templates here follow the latest XSL Working Draft and work on the latest version of XT. I doubt all of them work on IE5b2.

Feedback is welcome and encouraged

Generating HTML

XSL allows the result tree to be serialised as non-XML. In my case, I want to produce HTML 4.0 which isn't fully XML. Empty element tags need to be <BR>, for example, not <BR/>. To specify to XT (and other XSL processors) that the result tree is to be serialised as HTML 4.0, I use the following document element start-tag:

<xsl:stylesheet
  xmlns:xsl="http://www.w3.org/TR/WD-xsl"
  xmlns="http://www.w3.org/TR/REC-html40"
  result-ns=""
>

Note that the namespace attributes also indicate that I am using the "xsl" prefix for my XSL elements and no prefix for my result tree vocabulary. This is an arbitrary choice.

Generating XSL

I have a couple of stylesheets that are themselves generated by applying a stylesheet to an XML document. To distinguish the result vocabulary from the actual XSL used in the "meta" stylesheet, we (as always) use namespaces. One encounters the problem, though, that both namespaces end up having the same URI which makes them, in effect, the same namespace. Fortunately, XSL provides for this with a "quote:" before the URI used for the result namespace. So in my "meta" stylesheet for generating other stylesheets, I use as my document element start-tag:

<ixsl:stylesheet
  xmlns:ixsl="http://www.w3.org/TR/WD-xsl"
  xmlns:xsl="quote:http://www.w3.org/TR/WD-xsl"
  result-ns="xsl"
>

Again, the prefixes are arbitrary. Don't ask me why I use "ixsl". I had some reason at the time :-)

Invoking XT

I have a script:

~/java-rt/bin/jre -classpath ~/jars/rt.jar:~/jars/sax.jar:~/jars/xp.jar:~/jars/xt.jar \
	com.jclark.xsl.sax.Driver $1 $2 $3
  

that I use to invoke xt on my web host's Linux box. That way I can just type:

xt.sh filename-of-input filename-of-stylesheet filename-of-output
  

On a Windows platform, you can get an executable so you can just type:

xt filename-of-input filename-of-stylesheet filename-of-output
  

Actually, I use make, and my makefile has things like:

xmlsoftware-site/index.html: content/xmlsoftware/index.xml xsl/xmlsoftware/xmlsoftware.xsl \
		working/new-updated.xml working/date.xml
	scripts/xt.sh $< xsl/xmlsoftware/xmlsoftware.xsl $@
  

The HTML file has obvious dependencies on the XML file and the XSL stylesheet. The other two dependencies are automatically generated entities (for the what's new/updated and the date respectively) that are referenced in index.xml

The Templates

Here are the example templates, along with sample input and result.


Task

Transform Paras into Ps

Input

<Para>...</Para>

Output

<P>...</P>

Template(s)

<xsl:template match="Para">
  <P><xsl:apply-templates/></P>
</xsl:template>

Task

Transform Titles of Sections into H3s

Input

<Section>
  <Title>...</Title>
  ...
</Section>

Output

 ...
  <H3>...</H3>
  ...

Template(s)

<xsl:template match="Section/Title">
  <H3><xsl:apply-templates/></H3>
</xsl:template>

Task

Transform a Link with a URL attribute into an A with an HREF attribute

Input

<Link URL="http://www.jtauber.com/">My Site</Link>

Output

<A HREF="http://www.jtauber.com/">My Site</A>

Template(s)

<xsl:template match="Link">
  <A HREF="{@URL}"><xsl:apply-templates/></A>
</xsl:template>

Task

Transform a Category into a table row with the Title of the Category being a link to the category's page (named after the category's ID)

Input

<Category ID="xsl">
  <Title>XSL Editors / Tools</Title>
  ...
</Category>

Output

<TR><TD><A HREF="/xsl/">XSL Editors / Tools</A></TD></TR>

Template(s)

<xsl:template match="Category">
  <TR><TD><A HREF="/{@ID}/"><xsl:value-of select="Title"/></A></TD></TR>
</xsl:template>

Task

Transform an Email element to an A with mailto HREF and the email as the content too

Input

<Email>jtauber@jtauber.com</Email>

Output

<A HREF="mailto:jtauber@jtauber.com">jtauber@jtauber.com</A>

Template(s)

<xsl:template match="Email">
  <A HREF="mailto:{.}"><B><xsl:apply-templates/></B></A>
</xsl:template>

Notes

I could have used <xsl:value-of select="."/> instead of <xsl:apply-templates/> but this way I could have further markup in the Email element's content that gets transformed.


Task

Take Products in the list of new and updated products and generate a list item that includes a link to the product using the name of the product as the linking text and the category and ID of the product to form to URL of the link. Also include the version number of the product formatted as the CSS class "annotation"

Input

<NewUpdatedProduct>
  <Product ID="xt" Updated="1999-01-15">
    <Name>XT</Name>
    <Version>19990115</Version>
    <category>xsl</Category>
    ...
  </Product>
  ...
</NewUpdatedProduct>

Output

<UL>
  <LI><A HREF="/xsl/#xt">XT</A>
  <BR><SPAN CLASS="annotation">version: 19990115</SPAN></LI>
  ...
</UL>

Template(s)

<xsl:template match="NewUpdatedProduct">
  <UL><xsl:apply-templates/></UL>
</xsl:template>

<xsl:template match="NewUpdatedProduct/Product">
  <LI><A HREF="/{Category}/#{@ID}"><xsl:value-of select="Name"/></A>
  <BR/><SPAN CLASS="annotation">version: <xsl:value-of select="Version"/></SPAN>
  </LI>
</xsl:template>

Task

Produce an HTML document with a title specific to the product category unless it is not a product category page we are transforming.

Input

<ProductPage>
  <Title>XSL Editors / Tools</Title>
  ...
</ProductPage>

Output

<HTML>
  <HEAD>
    <TITLE>XSL Editors / Tools at XMLSOFTWARE</TITLE>
    ...
  </HEAD>
  ...
</HTML>

Input

<Page>
  ...
</Page>

Output

<HTML>
  <HEAD>
    <TITLE>XMLSOFTWARE: The XML Software Site</TITLE>
    ...
  </HEAD>
  ...
</HTML>

Template(s)

<xsl:template match="/">
  <HTML>
    <HEAD>
      <xsl:choose>
        <xsl:when test="ProductPage/Title">
          <TITLE><xsl:value-of select="ProductPage/Title"/> at XMLSOFTWARE</TITLE>
        </xsl:when>
        <xsl:otherwise>
          <TITLE>XMLSOFTWARE: The XML Software Site</TITLE>
        </xsl:otherwise>
      </xsl:choose>
      ...
    </HEAD>
    ...
  </HTML>
</xsl:template>

Task

Given a ProductSelect element with attribute Type equally "xsl", select Products from the ProductList whose Categories are "xsl".

Input

<ProductSelect Type="xsl"/>
...
<ProductList>
  <Product ID="xt" Updated="1999-01-15">
    <Name>XT</Name>
    ...
    <Category>xsl</Category>
    ...
  </Product>
</ProductList>

Output

the output is the transformed Products that are in the category xsl.

Template(s)

<xsl:template match="ProductSelect">
  <xsl:choose>
    ...
    <xsl:when test='.[@Type="xsl"]'>
      <xsl:apply-templates select='//ProductList/Product[Category="xsl"]'/>
    </xsl:when>
  </xsl:choose>
</xsl:template>

Notes

The template actually includes an xsl:when for each category. The entire template is itself generated by applying a stylesheet to the list of categories. See below for the template that does this.


Task

Generate the stylesheet template used above.

Input

<CategoryList>
  ...
  <Category ID="xsl">
    <Title>XSL Editors / Tools</Title>
    ...
  </Category>
</CategoryList>

Output

<xsl:template match="ProductSelect">
  <xsl:choose>
    ...
    <xsl:when test='.[@Type="xsl"]'>
      <xsl:apply-templates select='//ProductList/Product[Category="xsl"]'/>
    </xsl:when>
  </xsl:choose>
</xsl:template>

Template(s)

<ixsl:template match="CategoryList">
  <ixsl:element name="xsl:stylesheet">
    <ixsl:element name="xsl:template">
      <ixsl:attribute name="match">ProductSelect</ixsl:attribute>
      <ixsl:element name="xsl:choose">
        <ixsl:apply-templates/>
      </ixsl:element>
    </ixsl:element>
  </ixsl:element>
</ixsl:template>

<ixsl:template match="Category">
  <ixsl:element name="xsl:when">
    <ixsl:attribute name="test">.[@Type="<ixsl:value-of select="@ID"/>"]</ixsl:attribute>
    <ixsl:element name="xsl:apply-templates">
      <ixsl:attribute name="select"
        >ProductList/Product[Category="<ixsl:value-of select="@ID"/>"]</ixsl:attribute>
    </ixsl:element>
  </ixsl:element>
</ixsl:template>

Notes

This example uses XSL as the result vocabulary. This was achieved using the following document element start tag: <ixsl:stylesheet xmlns:ixsl="http://www.w3.org/TR/WD-xsl" xmlns:xsl="quote:http://www.w3.org/TR/WD-xsl" result-ns="xsl">


Task

Copy through any element that doesn't have a template. Include attributes and content.

Input

<AnyElement AnyAttribute="AnyValue">AnyContent</AnyElement>

Output

<AnyElement AnyAttribute="AnyValue">AnyContent</AnyElement>

Template(s)

<xsl:template match="*|@*" priority="-1">
  <xsl:copy>
    <xsl:apply-templates select="@*|*|text()"/>
  </xsl:copy>
</xsl:template>

Notes

This is used in a stylesheet that preprocessed an XML document before it is passed on to another stylesheet. Only the odd element is altered, hence the need for this "let everything else through" template.