XSLT as a Templating System

XSL is a programming language (which uses an XML-compatible syntax) used to describe a transform from an XML file to another XML file (though it may also be used to generate non-XML documents with a little bit of hacking).

Consider a simple XML file describing a filesystem tree: every node contains a name and optionally some children.

<dir name="home">
  <dir name="arkadir">
    <file name=".emacs" />
    <file name=".zshrc" />
  </dir>
  <dir name="root">
    <file name="TWiki.tar.gz"/>
  </dir>
</dir>

One would wish to convert this to bit of XHTML such as the following:

<ul><li><p>home</p>
  <ul><li><p>arkadir</p>
    <ul><li><p>.emacs</p></li>
        <li><p>.zshrc</p></li>
    </ul></li>
        <li><p>root</p>
    <ul><li><p>TWiki.tar.gz</p></li>
    </ul></li>
  </ul></li>
</ul>

A stylesheet to perform that transform would be:

<xsl:template match="dir">
  <li>
    <p><xsl:value-of select="@name"/></p>
    <ul><xsl:apply-templates/></ul>
  </li>
</xsl:template> 

<xsl:template match="file">
  <li><p><xsl:value-of select="@name"/></p></li>
</xsl:template> 

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

Stylesheets work based on templates, where every element in the source tree may be associated with one or more templates. Whenever a template matches the currently explored node, the code for that template is inserted into the output tree, with the special elements in the template being replaced with the appropriate values : xsl:value-of inserts the text value of an attribute or XSL variable, or the text contents of an element, while xsl:apply-template will traverse the child nodes and attempt to match them with templates. The replace-with-result semantics allow the writer to specify how the generated code should be wrapped at every level.

Making it real

To perform any kind of serious work in a programming language, one needs to have access to abstraction functionality: the ability to factor identical code into code blocks (xsl:template), the ability to make abstractions based on parameters (xsl:param), and the ability to separate the code into different files (xsl:import).

An issue appears from the fact that xsl:apply-templates will apply all available templates for the given elements, which makes it difficult to factor out code (for instance, using distinct templates for a given element when generating a quick view and a full view within the same file). The element xsl:for-each can be used to inline that behavior, but it still prevents factoring.

The solution comes from calling templates:

<xsl:template name="long-dir">
  <li>
    <p><xsl:value-of select="@name"/></p>
    <ul>
      <xsl:for-each select="dir">
        <xsl:call-template name="long-dir"/>
      </xsl:for-each>
    </ul>
  </li>
</xsl:template> 

<xsl:template name="short-dir">
  {
  <xsl:value-of select="@name"/>
  <xsl:for-each select="dir">
    <xsl:call-template name="short-dir"/>
  </xsl:for-each>
  }
</xsl:template>

Instead of executing templates based on matching the input elements by XPath, one can select the appropriate elements using xsl:for-each and then use the appropriate template with xsl:call-template. The code above, for instance, uses this property to define two recursive directory printing templates, one which uses lists and the other which uses text-mode with delimiting brackets (and an output of { home { arkadir } { root } } for the example above).

Using helpers

The ability to import and call individual templates makes it possible to create libraries of helpers that can be included at will. For instance, to insert a button that hides/shows an element, one could write the following template:

<!-- A template that inserts a show/hide toggle button.
     Param 'target' : the identifier of the target element.
     Param 'show' : the display style when visible. -->
<xsl:template name="toggle">
  <xsl:param name="target"/>
  <xsl:param name="show"/>
  <button style="width:20px ; height:20px ; float:left">
    <xsl:attribute name="onclick">
      toggle('<xsl:value-of select="$target"/>','<xsl:value-of select="$show"/>');
    </xsl:attribute>
  </button>
</xsl:template> 

<xsl:template match="dir">
  <!-- Generate an unique identifier -->
  <xsl:variable name="id"><xsl:value-of select="generate-id(.)"/></xsl:variable>
  <xsl:variable name="show">block</xsl:variable>
  <li>
    <!-- Insert a toggle button -->
    <xsl:call-template name="toggle">
      <xsl:with-param name="target" select="$id"/>
      <xsl:with-param name="show" select="$show"/>
    </xsl:call-template>
    <p><xsl:value-of select="@name"/></p>
    <ul>
      <!-- Specify the ID of the 'ul' element to bind it
           to the toggle button. -->
      <xsl:attribute name="id">
        <xsl:value-of select="$id"/>
      </xsl:attribute>
      <xsl:apply-templates/>
    </ul>
  </li>
</xsl:template> 

<xsl:template match="file">
  <li><p><xsl:value-of select="@name"/></p></li>
</xsl:template> 

<xsl:template match="/">
  <!-- Insert the script containing the toggle function -->
  <script>
function toggle(id, show)
{
  var target = document.getElementById(id);
  if (target.style.display == 'none')
    target.style.display = show;
  else
    target.style.display = 'none';
}
  </script>
  <ul><xsl:apply-templates/></ul>
</xsl:template>

Assuming that the toggle function (written in javascript) is provided in the resulting file, one can simply bind a new toggle button to any element on the page, and the button will then automatically perform show-hide operations on that element.

Templating system

A templating system is plugged at the output end of a web site. The web site generates print instructions (in the form of function calls, native objects or generated XML) that are then processed by the templating system and turned into an XHTML document that is sent to the user.

Zend views apply this process with native objects : the data required for rendering is inserted into a Zend_View object, and a template is a PHP file which reads data from the view object and wraps it in XHTML.

When using XSL templates, the web site generates an XML file which is then processed by the templates. The process is intrinsically slower than using PHP objects (because one has to generate the XML file, then invoke the processor, as opposed to loading up an object and using the same interpreter to turn it into XHTML), but can be performed on the client side and thus make the server load lighter. As a bonus, since the generated output is made up of only strings, it’s possible to cache the parts that do not change often in a database without requiring complex serialization and parsing setups.

0 Responses to “XSLT as a Templating System”


  1. No Comments

Leave a Reply

Your email address will not be published. Required fields are marked *

*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>



659 feed subscribers
(readers who polled a feed this week)