# Monday, November 13, 2006
« Perfect Laptop | Main | Just a little bit more XSLT (the files) »

I've hacked on a little bit about XSLT and I think it's about time I added some more.  So this post will serve as the next instalment, and somewhat of an introduction to the uninitiated.

Lets say we were to consider the following data:

Position Horse Jockey
1 Delta Blues (JPN) Y. Iwata
2 Pop Rock (JPN) D. Oliver
3 Maybe Better (AUS) C. Munce
4 Zipping (AUS) G. Boss
5 Land 'n Stars (GB) J. Egan
6 Mahtoum (AUS) C. Brown
7 Yeats (IRE) K. Fallon
8 Activation (NZ) M. Rodd
9 Mandela (NZ) C. Williams
10 Glistening (GB) S. Seamer

As XML, it could concievably arrive like this:

<raceResults>
<finish result="1" jockey="Y. Iwata" originISO="JPN">Delta Blues</finish>
<finish result="2" jockey="D. Oliver" originISO="JPN">Pop Rock</finish>
<finish result="3" jockey="C. Munce" originISO="AUS">Maybe Better</finish>
<finish result="4" jockey="G. Boss" originISO="AUS">Zipping</finish>
<finish result="5" jockey="J. Egan" originISO="GB">Land 'n Stars</finish>
<finish result="6" jockey="C. Brown" originISO="AUS">Mahtoum</finish>
<finish result="7" jockey="K. Fallon" originISO="IRE">Yeats</finish>
<finish result="8" jockey="M. Rodd" originISO="NZ">Activation</finish>
<finish result="9" jockey="C. Williams" originISO="NZ">Mandela</finish>
<finish result="10" jockey="S. Seamer" originISO="GB">Glistening</finish>
</raceResults>

And that is fine, and may well even be useful.  The place where we should introduce XSLT is when we would like to do something with it like turning that XML into text, HTML, or more XML.

Text is an easy one to start with.  Consider the following stylesheet:

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
<xsl:output method="text"/>

<xsl:template match="/">
   <xsl:apply-templates select="raceResults"/>
</xsl:template>

<xsl:template match="raceResults">
position,jockey,horse,
<xsl:apply-templates select="finish"/>
</xsl:template>

<xsl:template match="finish">
   <xsl:value-of select="@result"/>,<xsl:value-of select="@jockey"/>,<xsl:value-of select="."/>,
</xsl:template>

</xsl:stylesheet>

When applied to our XML, it will produce the following result:

position,jockey,horse,
1,Y. Iwata,Delta Blues,
2,D. Oliver,Pop Rock,
3,C. Munce,Maybe Better,
4,G. Boss,Zipping,
5,J. Egan,Land 'n Stars,
6,C. Brown,Mahtoum,
7,K. Fallon,Yeats,
8,M. Rodd,Activation,
9,C. Williams,Mandela,
10,S. Seamer,Glistening,

The trick is first to not look for flow control in the traditional fashion (For loops, etc).  Instead the XSLT processor will treat it in the following manner:

  1. Start at the top of the source document - denoted with a /
  2. Every time you find a raceResults do what is in the raceResults template.
  3. (now inside the raceResults template) output the literal string: position,jockey,horse,
  4. (still inside the raceResults template) every time you find a finish, do what is in the finish template
  5. (now inside the finish template) Pick out the attribute called result, then pick out the attribute called jockey, then pick out what ever is contained in this element.  NB:  Attributes are denoted by the @

And that is it!

Now did you notice how literal text was handled?  All the bits of your output can live inside the template.  Now if we wanted an HTML representation we would use the following template:

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
<xsl:output method="html"/>

<xsl:template match="/">
   <html><head><title>Race Results</title></head><body>
   <xsl:apply-templates select="raceResults"/>
   </body></html>
</xsl:template>

<xsl:template match="raceResults">
<xsl:apply-templates select="finish"/>
</xsl:template>

<xsl:template match="finish">
   <p>Position <b><xsl:value-of select="@result"/></b>was <b><xsl:value-of select="."/></b>ridden by <b><xsl:value-of select="@jockey"/></b></p>
</xsl:template>

</xsl:stylesheet>

...and following exactly the same steps as the text example, the XSLT processor will output the following HTML:

Position 1 was Delta Blues ridden by Y. Iwata

Position 2 was Pop Rock ridden by D. Oliver

Position 3 was Maybe Better ridden by C. Munce

Position 4 was Zipping ridden by G. Boss

Position 5 was Land 'n Stars ridden by J. Egan

Position 6 was Mahtoum ridden by C. Brown

Position 7 was Yeats ridden by K. Fallon

Position 8 was Activation ridden by M. Rodd

Position 9 was Mandela ridden by C. Williams

Position 10 was Glistening ridden by S. Seamer

As you can imagine, it can be easy to get a stylesheet for HTML output that contains a lot of HTML markup!  The important thing to note in this example is that the HTML inside the stylesheet must be valid XML as well as valid HTML.  Note the closing </p> which would normally be optional in HTML is manditory here.

Now we get to the fun example:  Turning XML into XML :D

Why would we want to turn XML into XML?  For as many reasons as you have XML in your life!  Imagine turning the above document into its own RSS feed for example.

We could use the following template:

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
<xsl:output method="xml" indent="yes"/>

<xsl:template match="/">

<rss version="2.0">
   <channel>
      <title>Race News</title>
      <link>http://www.horses.com/</link>
      <description>Live race results as they cross the finish line.</description>
      <language>en-us</language>
      <pubDate>Tue, 13 Nov 2006 17:45:00 AEST</pubDate>
      <lastBuildDate>Tue, 13 Nov 2006 17:45:00 AEST</lastBuildDate>
      <docs>http://blogs.law.harvard.edu/tech/rss</docs>
      <generator>My RSS transformation</generator>
      <managingEditor>james@deepdark.net</managingEditor>
      <webMaster>james@deepdark.net</webMaster>
     
   <xsl:apply-templates select="raceResults"/>
  
   </channel>
</rss>

</xsl:template>

<xsl:template match="raceResults">
<xsl:apply-templates select="finish"/>
</xsl:template>

<xsl:template match="finish">
      <item>
         <title><xsl:value-of select="."/></title>
         <link>http://www.horses.com/lookup?<xsl:value-of select="."/></link>
         <description>Registered in: <xsl:value-of select="@originISO"/> .  Ridden by: <xsl:value-of select="@jockey"/></description>
         <pubDate>Tue, 13 Nov 2006 17:45:00 AEST</pubDate>
         <guid>http://www.horses.com/results/race6273-<xsl:value-of select="@result"/></guid>
      </item>
</xsl:template>
</xsl:stylesheet>

This example is the reason I took you so far down the page.  It builds on the key points of the prior two examples: 

  1. The output can mingle in stylesheet, so long as it is valid XML.  I hope you have noticed that the stylesheet itself is valid XML too.
  2. Again there are no flow control pieces.  In fact is is using exactly the same 5 step process I outlined for the first example (you guessed it, this was no mistake :)

The output is the following lovely RSS feed:

<?xml version="1.0"?>
<rss version="2.0">
<channel>
<title>Race News</title>
<link>http://www.horses.com/</link>
<description>Live race results as they cross the finish line.</description>
<language>en-us</language>
<pubDate>Tue, 13 Nov 2006 17:45:00 AEST</pubDate>
<lastBuildDate>Tue, 13 Nov 2006 17:45:00 AEST</lastBuildDate>
<docs>http://blogs.law.harvard.edu/tech/rss</docs>
<generator>My RSS transformation</generator>
<managingEditor>james@deepdark.net</managingEditor>
<webMaster>james@deepdark.net</webMaster>
<item>
<title>Delta Blues</title>
<link>http://www.horses.com/lookup?Delta Blues</link>
<description>Registered in: JPN .  Ridden by: Y. Iwata</description>
<pubDate>Tue, 13 Nov 2006 17:45:00 AEST</pubDate>
<guid>http://www.horses.com/results/race6273-1</guid>
</item>
<item>
<title>Pop Rock</title>
<link>http://www.horses.com/lookup?Pop Rock</link>
<description>Registered in: JPN .  Ridden by: D. Oliver</description>
<pubDate>Tue, 13 Nov 2006 17:45:00 AEST</pubDate>
<guid>http://www.horses.com/results/race6273-2</guid>
</item>
<item>
<title>Maybe Better</title>
<link>http://www.horses.com/lookup?Maybe Better</link>
<description>Registered in: AUS .  Ridden by: C. Munce</description>
<pubDate>Tue, 13 Nov 2006 17:45:00 AEST</pubDate>
<guid>http://www.horses.com/results/race6273-3</guid>
</item>
<item>
<title>Zipping</title>
<link>http://www.horses.com/lookup?Zipping</link>
<description>Registered in: AUS .  Ridden by: G. Boss</description>
<pubDate>Tue, 13 Nov 2006 17:45:00 AEST</pubDate>
<guid>http://www.horses.com/results/race6273-4</guid>
</item>
<item>
<title>Land 'n Stars</title>
<link>http://www.horses.com/lookup?Land 'n Stars</link>
<description>Registered in: GB .  Ridden by: J. Egan</description>
<pubDate>Tue, 13 Nov 2006 17:45:00 AEST</pubDate>
<guid>http://www.horses.com/results/race6273-5</guid>
</item>
<item>
<title>Mahtoum</title>
<link>http://www.horses.com/lookup?Mahtoum</link>
<description>Registered in: AUS .  Ridden by: C. Brown</description>
<pubDate>Tue, 13 Nov 2006 17:45:00 AEST</pubDate>
<guid>http://www.horses.com/results/race6273-6</guid>
</item>
<item>
<title>Yeats</title>
<link>http://www.horses.com/lookup?Yeats</link>
<description>Registered in: IRE .  Ridden by: K. Fallon</description>
<pubDate>Tue, 13 Nov 2006 17:45:00 AEST</pubDate>
<guid>http://www.horses.com/results/race6273-7</guid>
</item>
<item>
<title>Activation</title>
<link>http://www.horses.com/lookup?Activation</link>
<description>Registered in: NZ .  Ridden by: M. Rodd</description>
<pubDate>Tue, 13 Nov 2006 17:45:00 AEST</pubDate>
<guid>http://www.horses.com/results/race6273-8</guid>
</item>
<item>
<title>Mandela</title>
<link>http://www.horses.com/lookup?Mandela</link>
<description>Registered in: NZ .  Ridden by: C. Williams</description>
<pubDate>Tue, 13 Nov 2006 17:45:00 AEST</pubDate>
<guid>http://www.horses.com/results/race6273-9</guid>
</item>
<item>
<title>Glistening</title>
<link>http://www.horses.com/lookup?Glistening</link>
<description>Registered in: GB .  Ridden by: S. Seamer</description>
<pubDate>Tue, 13 Nov 2006 17:45:00 AEST</pubDate>
<guid>http://www.horses.com/results/race6273-10</guid>
</item>
</channel>
</rss>

I think this technique is going to be ever more important in the future.  Take in some SOAP, spit out some HTML.  Take in some XML from one API, spit it out as RSS.  A great tool for the toolbox.