Behavior, Content, Money – 3 Things you should never give away for free!!!

BCmoney MobileTV

A quick XSLT tutorial

Posted by bryan on August 21, 2009 in HTML, XML with 3 Comments


No Gravatar
Diagram of the basic elements and processing f...

Image via Wikipedia

This post is meant to be a brief XSLT refresher, since I tend to need to rely on several web resources as references to remember the obscure functions and element names available in various versions of the XSL specification, as well as to remind me of the various patterns, tricks and workarounds.

 

There are three key technologies at play in an XSL Transformation:

  1. XML – the original document holding the data to be parsed/processed/rendered
  2. XSL – the definition of a transformation and any additional styles to apply
  3. HTML – the human-readable rendering of the original XML data, which gets displayed to the end-user

As you can see in the diagram on the right, XSLT follows a clear processing path, starting with an XML document and the corresponding XSL document designed to parse it provided to an XSLT Processor, which acts as an intermediary in applying the desired styles and structure to the particular data being parsed out of the XML. The resulting document could technically be any number of formats, including another XML document with a different format, Plaintext, CSV, HTML, xHTML, RDFa, JSON, HTML with microformats, CSS, Javacript or even Java, PHP or another server-side language’s class files to be compiled or interpreted later.

Typically though, HTML will be the format displayed by a rendering, and officially only XML and HTML output formats are supported by the specification, although even in the browser, it is possible to render other formats to be displayed by not specifying the output type.

You apply xPath within a select attribute to navigate the nodes in the XML document. You don’t need to know much about xPath to start playing with XSLT and process some basic documents, however more complex patterns could prove difficult without a more thorough understanding.
Here’s my quick crash course:

  • ELEMENTS – accessed by using a “/” followed by the element name
    You can grab the Element as an “object” representation of the tag, as in the following snippet
    <book>My Interesting Story</book>
    <book>Introduction to xPath</book>
    <book>XML for Dummies</book>
    by using the command
    /book
    (note that if book contains attributes or child elements, they would all be part of the enumerated list of Elements that we accessed via this command)

  • ATTRIBUTES – accessed by using “@” followed by the attribute name
    You could get the server status attribute values from the XML snippet
    <server status=”active”>IDST-101234</server>
    <server status=”down”>IDST-101235</server>
    <server status=”running”>LAB-BACKUP</server>

    by using the command
    /server/@status
    (you can also check that an attribute is equal to some particular value, such as checking for servers that are down, using the following syntax: /server[@status=’down’])

  • String manipulations – You can perform a number of xPath functions
    (the functions you can use depends heavily on the xPath implementation version within the XSLT Processor engine, and you can even extend and write your own functions in xPath 2.0+, however for simplicity’s sake and for best cross-browser results, you’d be best to avoid these altogether or stick to the core functions)

 
Check out the following resources to quickly get up to speed beyond this basic intro:

  1. Familiarize yourself with the xPath Syntax
  2. Microsoft Developer Network (MSDN’s) xPath Examples
  3. Try out your own xPath easily with the online xPath Expression checker tool

 
In order to use xPath together with XSLT, you have to apply a template or value selection, for example, to get the TEXT of a particular ELEMENT, you would using the text() command within a standard XSL value selection tag <xsl:value-of select=”text()”/>
This is where xPath and XSLT come together.

For example, you can grab the data from within the following <title> tag as follows
<title>My Interesting Data</title>
by using the command
<xsl:template match=”/title”>
<xsl:value-of select=”text()”/>
</xsl:template>

So that’s XSLT (and xPath) in a nutshell. At this point, it would be useful to see a practical example. We’ll process something useful like a shareable Address Book (I really wish that someone in the TelCo new product development space could get this right so I don’t need to update my entire set of contacts everytime I switch my phone, but that’s another story). To accomplish this, we’ll start with a basic XML format as follows:

 <AddressBook>
  <contact type="friend">
    <name>Phil Hart</name>
    <title>Social Worker, Federal Government of Countria</title>
    <email>phil.hart@countria.gov</email>
    <phone extension="3490">+23 (81) 892-2387</phone>
  </contact>
   ...
 </AddressBook>

 

 

In order to grab the desired data and render it in a pleasant view for the end-user, we can apply the following XSL:

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0" xmlns="http://www.w3.org/1999/xhtml">
  <xsl:output method="html" version="4.0" encoding="utf-8" indent="yes"/>
   <xsl:template match="/">
   <html xmlns="http://www.w3.org/1999/xhtml">
   <head>
     <title>Address Book - Business Cards</title>
     <style type="text/css">
      ul { width:400px; list-style-type:none }
      li { border:1px solid #ccc; margin-bottom:20px; padding-left:5px }
     </style>
   </head>
   <body>
     <ul>
       <xsl:for-each select="AddressBook/contact[@type='friend']">
         <xsl:apply-templates select="name"/>
         <xsl:apply-templates select="title"/>
         <xsl:apply-templates select="email"/>
         <xsl:apply-templates select="phone"/>
       </xsl:for-each>
     </ul>
   </body>
   </html>
   </xsl:template>   
   <xsl:template match="name">
      <li>
        <h1><xsl:value-of select="text()"/></h1>
   </xsl:template>
   <xsl:template match="title">
        Occupation: <h3><em><xsl:value-of select="text()"/></em></h3>
   </xsl:template>
   <xsl:template match="email">
        <p>Email: <a href="mailto:<xsl:value-of select="text()"/>"><tt><xsl:value-of select="text()"/></tt></a></p>
   </xsl:template>
   <xsl:template match="phone">
     <xsl:variable name="extension" select='@extension' />
     <xsl:variable name="phone" select="text()"/>
     <xsl:variable name="thephone">
       <xsl:call-template name="replaceCharsInString">
         <xsl:with-param name="stringIn" select="string($phone)"/>
         <xsl:with-param name="charsIn" select="' '"/>
         <xsl:with-param name="charsOut" select="'-'"/>
       </xsl:call-template>
     </xsl:variable>
     <xsl:variable name="phonenum">
       <xsl:call-template name="replaceCharsInString">
         <xsl:with-param name="stringIn" select="string($thephone)"/>
         <xsl:with-param name="charsIn" select="'('"/>
         <xsl:with-param name="charsOut" select="''"/>
       </xsl:call-template>
     </xsl:variable>
     <xsl:variable name="phonenumber">
       <xsl:call-template name="replaceCharsInString">
         <xsl:with-param name="stringIn" select="string($phonenum)"/>
         <xsl:with-param name="charsIn" select="')'"/>
         <xsl:with-param name="charsOut" select="''"/>
       </xsl:call-template>
     </xsl:variable>     
     <xsl:choose>
      <xsl:when test="extension != ''">        
          <p>Phone#: <a href="tel:<xsl:value-of select="$phonenumber"/>"><xsl:value-of select="$phone"/>ext. <xsl:value-of select="extension" /></a></p>
      </xsl:when>
      <xsl:otherwise>
          <p>Phone#: <a href="tel:<xsl:value-of select="$phonenumber"/>"><xsl:value-of select="$phone"/></a></p>
      </xsl:otherwise>
     </xsl:choose>
      </li>
   </xsl:template>
   <!-- a useful template for string replacement -->
    <xsl:template name="replaceCharsInString">
      <xsl:param name="stringIn"/>
      <xsl:param name="charsIn"/>
      <xsl:param name="charsOut"/>
      <xsl:choose>
        <xsl:when test="contains($stringIn,$charsIn)">
          <xsl:value-of select="concat(substring-before($stringIn,$charsIn),$charsOut)"/>
          <xsl:call-template name="replaceCharsInString">
            <xsl:with-param name="stringIn" select="substring-after($stringIn,$charsIn)"/>
            <xsl:with-param name="charsIn" select="$charsIn"/>
            <xsl:with-param name="charsOut" select="$charsOut"/>
          </xsl:call-template>
        </xsl:when>
        <xsl:otherwise>
          <xsl:value-of select="$stringIn"/>
        </xsl:otherwise>
      </xsl:choose>
    </xsl:template>   
 </xsl:stylesheet>

 

 

All that XSL was to provide the end-user with a nice to look at “Business Card”-esque view of your Address Book, with the following HTML structure:

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
 <head>
   <title>Address Book - Business Cards</title>
   <style type="text/css">
    ul { width:400px; list-style-type:none }
    li { border:1px solid #ccc; margin-bottom:20px; padding-left:5px }
   </style>
 </head>
 <body>
   <ul>
     <li>
       <h1>Phil Hart</h1>
       Occupation: <h3><em>Social Worker, Federal Government of Countria</em></h3>
       <p>Email: <a href="mailto:phil.hart@countria.gov"><tt>phil.hart@countria.gov</tt></a></p>
          <p>Phone#: <a href="tel:+23-81-892-2387">+23 (81) 892-2387</a></p>
     </li>
     <li>
       <h1>Barb Boe</h1>
       Occupation: <h3><em>Secretary, Company Z Inc.</em></h3>
       <p>Email: <a href="mailto:barb.boe@companyz.com"><tt>barb.boe@companyz.com</tt></a></p>
          <p>Phone#: <a href="tel:+1-98-765-4321">+1 (98) 765-4321</a></p>
     </li>
   </ul>
 </body>
</html>

That’s all there is to it. Mind you, in the XSL there’s some complex stuff going on because we reformat the string we get from the <phone> element, in order to make it a mobile-friendly clickable link, that follows the tel protocol; but all in all it wasn’t too bad as we only had to apply a string replacement template three times (which can probably be even further optimized if you dug up or cooked up a regular expression string replacement, but that’s an exercise for the reader).
You can view the full XML, or the XSL. As usual a demos and download is also available:


-or-


Lastly, I wanted to provide a table showing which browsers support which versions of XSLT fully:

XML, XSL and XSLT Support
Feature IE Safari Firefox Chrome Opera
XML 5 3 2 1 8
XSL 6 3 3 1 9
XSLT 6 4 3.5 1 9

Source: W3schools

XSL-FO processors are not available natively in any browser at this time, however it may be possible to “fool” an XSLT render to display some elements of XSL-FO as HTML output and vice versa, as described in this IBM article “HTML to Formatting Objects (FO) conversion guide” on how to pre-render one the server-side and present nicely formatted PDF, HTML, Print-optimized, and other formatted documents to end-users. One of the leading open source tools for doing so is Apache FOP.