XSLT Inheritance

2

March 11, 2012 by Alistair Deneys

I like XSLT. Although if you’ve ever visited my codeflood website and had a look at my SashimiCMS you’d have guessed that. SashimiCMS is a CMS I created many years ago, all based on XML and XSLT. In fact, I currently use that CMS to manage codeflood.

XSLT is a great tool for working with XML and transforming it. So it’s always a shame when I see Sitecore projects that avoid XSLT (renderings). XSLT can be quite foreign for C# developers. This is because unlike C# which is considered procedural (do this, then to this, then do this), XSLT is declarative (when this happens do this, when this happens do this, when this happens do this). It’s a very different mindset to get into. But once you tap it correctly, it’s quite powerful, for the right problem. XSLT is a very specific language and was never designed to do all things, which is why at times we need to drop out to extensions (C# methods) to get stuff done.

What it comes down to is, use the right tool for the right job. Use XSLTs for their strength, which is processing XML. Yes you could do it in C# code, but it may be more complicated than if you take the pill and use XSLT instead. Especially when working on hierarchical data.

This post is going to cover a few points to writing “good” XSLT stylesheets and make the maintainable. Let’s first take a look at how we can reuse common xsl:templates across multiple files. And no, copy/paste inheritance is not the answer.

xsl:import and xsl:include allow you to extract common templates to a separate file and reuse them. So what’s the difference between xsl:import and xsl:include? Just the importance of the templates that are brought in from the external file. When you use xsl:include, it’s as if the templates from the external file existed within the current file, so the included templates have the same importance as the templates of the current file. When you use xsl:import, the imported templates have less importance than the current file’s templates. If the imported file had a template the same as the current file, the current file template would be used, but if xsl:include was used the transform engine would throw an error that it saw two templates that match with the same importance.

Let’s take a look at an example of extracting common templates.

Let’s create a new XSLT file called DataUtil.xslt that contains the following code:

<?xml version="1.0" encoding="UTF-8"?>

<xsl:stylesheet version="1.0"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
  xmlns:sc="http://www.sitecore.net/sc" 
  exclude-result-prefixes="sc">

  <xsl:template name="renderTitle">
    <xsl:choose>
      <xsl:when test="sc:fld('title',.) != ''">
        <sc:text field="title"/>
      </xsl:when>
      <xsl:otherwise>
        <xsl:value-of select="@name"/>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:template>

</xsl:stylesheet>

The above code contains a single xsl:template used to render the title of a Sitecore item. If the title field is empty it will fallback to use the item’s name instead.

Note how I’ve missed all that normal Sitecore boilerplate? That’s because this file is not a Sitecore rendering. I don’t intend to use it directly but instead it will be included in other renderings.

Now let’s create a new Sitecore rendering which includes the DataUtil.xslt common templates.

<?xml version="1.0" encoding="UTF-8"?>

<xsl:stylesheet version="1.0"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
  xmlns:sc="http://www.sitecore.net/sc" 
  exclude-result-prefixes="sc">

  <xsl:include href="DataUtil.xslt"/>

  <!-- output directives -->
  <xsl:output method="html" indent="no" encoding="UTF-8" />

  <!-- parameters -->
  <xsl:param name="lang" select="'en'"/>
  <xsl:param name="id" select="''"/>
  <xsl:param name="sc_item"/>
  <xsl:param name="sc_currentitem"/>

  <!-- entry point -->
  <xsl:template match="*">
    <xsl:apply-templates select="$sc_item" mode="main"/>
  </xsl:template>

  <!--========================================================-->
  <!-- main& -->
  <!--========================================================-->
  <xsl:template match="*" mode="main">
    <xsl:call-template name="renderTitle"/>
  </xsl:template>

</xsl:stylesheet>

By extracting common xsl:templates such as the one above, we’ve allowed those templates to be reused in any other xsl:template and gained all the benefits of sharing a common piece of code between multiple files:

  • Code using the common templates will be more consistent as they don’t have to rewrite the same code.
  • Maintenance of the code is much easier as it only exists in a single file.

The above code is only one example of a common xsl:template. I bet you could come up with a number of these just by looking through your current project, such as creating a link to an item (output the item title if supplied, otherwise just output the item name).

On the projects I do see using XSLT in Sitecore, it’s not often I see a good structure or technical design for the XSLT. How often have you seen this kind of rendering in Sitecore?

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
  xmlns:sc="http://www.sitecore.net/sc" 
  exclude-result-prefixes="sc">

  <!-- output directives -->
  <xsl:output method="html" indent="no" encoding="UTF-8" />

  <!-- parameters -->
  <xsl:param name="lang" select="'en'"/>
  <xsl:param name="id" select="''"/>
  <xsl:param name="sc_item"/>
  <xsl:param name="sc_currentitem"/>

  <!-- entry point -->
  <xsl:template match="*">
    <xsl:apply-templates select="$sc_item" mode="main"/>
  </xsl:template>

  <!--==============================================================-->
  <!-- main -->
  <!--==============================================================-->
  <xsl:template match="*" mode="main">
    <h1>
      <sc:text field="title"/>
    </h1>
    <xsl:if test="test">
      <!-- 100 lines of code -->
      ...
    </xsl:if>
    <!-- 100 more lines of code -->
    ...
  </xsl:template>
</xsl:stylesheet>

Sitecore provides a nice starting point for renderings but many developers don’t extend beyond that very well.

A good XSLT will make appropriate use of xsl:templates to process different elements of input, in the same fashion as a good C# class will make appropriate use of methods to break apart complex processing tasks.

For example, let’s consider the following XSLT which is used to generate a simple menu from the Sitecore content tree (single level).

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  xmlns:sc="http://www.sitecore.net/sc"
  exclude-result-prefixes="sc">

  <!-- output directives -->
  <xsl:output method="html" indent="no" encoding="UTF-8" />

  <!-- parameters -->
  <xsl:param name="lang" select="'en'"/>
  <xsl:param name="id" select="''"/>
  <xsl:param name="sc_item"/>
  <xsl:param name="sc_currentitem"/>

  <!-- entry point -->
  <xsl:template match="*">
    <xsl:apply-templates select="$sc_item" mode="main"/>
  </xsl:template>

  <!--========================================================-->
  <!-- main -->
  <!--========================================================-->
  <xsl:template match="*" mode="main">
    <xsl:variable name="root" select="ancestor-or-self::item[@key='home']"/>
    <ul>
      <xsl:for-each select="$root|$root/child::item">
        <li>
          <sc:link/>
        </li>
      </xsl:for-each>
    </ul>
  </xsl:template>
</xsl:stylesheet>

This code could be made better by splitting out the part that renders the link, from the part that walks the content tree.

<xsl:template match="*" mode="main">
  <xsl:variable name="root" select="ancestor-or-self::item[@key='home']"/>
  <ul>
    <xsl:for-each select="$root|$root/child::item">
      <xsl:call-template name="renderLink"/>
    </xsl:for-each>
  </ul>
</xsl:template>

<xsl:template name="renderLink">
  <li>
    <sc:link/>
  </li>
</xsl:template>

In the above code we’ve split the link generation out to it’s own xsl:template. This makes the code easier to understand, but also makes it easier to reuse and override.

Let’s say we now have a new requirement on a project for a new kind of menu control. It should traverse the content tree in the same way as the above menu, but instead of outputting simple links it should wrap each link in a div and a span (to allow appropriate styling through CSS). There are two approaches you could take to satisfy this new requirement while leveraging the existing control (and hence, reducing the amount of effort required to deliver the requirement).

Firstly, let’s add some parameters to the XSLT to allow specifying whether the link should be wrapped and by what.

<xsl:param name="wrap1" select="'div'" />
<xsl:param name="wrap2" select="'span'" />

And now to update the renderLink xsl:template to output the required tags when the parameters are used.

<xsl:template name="renderLink">
  <li>
    <xsl:if test="$wrap1!=''">
      <xsl:value-of select="concat('&lt;', $wrap1, '&gt;')"
        disable-output-escaping="yes"/>
    </xsl:if>
    <xsl:if test="$wrap2!=''">
      <xsl:value-of select="concat('&lt;', $wrap2, '&gt;')"
        disable-output-escaping="yes"/>
    </xsl:if>
    <sc:link/>
    <xsl:if test="$wrap2!=''">
      <xsl:value-of select="concat('&lt;/', $wrap2, '&gt;')"
        disable-output-escaping="yes"/>
    </xsl:if>
    <xsl:if test="$wrap1!=''">
      <xsl:value-of select="concat('&lt;/', $wrap1, '&gt;')"
        disable-output-escaping="yes"/>
    </xsl:if>
  </li>
</xsl:template>

Looks kind of messy right? And I’m not too sure about manually generating tags through textual output (rather than just specifying the tag directly).

A cleaner approach is to create a new rendering, but make use of the existing XSLT and override the xsl:templates that need to change.

<?xml version="1.0" encoding="UTF-8"?>

<xsl:stylesheet version="1.0"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  xmlns:sc="http://www.sitecore.net/sc"
  exclude-result-prefixes="sc">

  <xsl:import href="Simple Menu.xslt"/>

  <!-- output directives -->
  <xsl:output method="html" indent="no" encoding="UTF-8" />

  <!-- parameters -->
  <xsl:param name="lang" select="'en'"/>
  <xsl:param name="id" select="''"/>
  <xsl:param name="sc_item"/>
  <xsl:param name="sc_currentitem"/>

  <xsl:template name="renderLink">
    <li>
      <div>
        <span>
          <sc:link/>
        </span>
      </div>
    </li>
  </xsl:template>
</xsl:stylesheet>

Note above how we’ve used xsl:import so the templates defined in the XSLT will take priority over those in the imported file. And because we’re making use of the Simple Menu.xslt from above, we don’t need to specify templates that don’t change in this file, such as the default Sitecore provided main mode template or the content tree traversal template.

The above code does the same as the first revision, but I think it looks much cleaner. By splitting the piece of code that generates the link out to a separate template it’s much easier to override the renderLink template and adjust the output per link. This is the same kind of benefit we gain when using inheritance in C# to override specific methods.

I hope this post will make you think carefully about the structure of your xsl:templates next time you’re creating an XSLT. And if you don’t use renderings in your Sitecore projects I hope this motivates you to give them a go.

Advertisements

2 thoughts on “XSLT Inheritance

  1. briancaos says:

    I quit using XSLT’s a few years ago because they tend to drift into maintenance nightmares. Most of the major mistakes we experienced in Pentia was XSLT’s that had grown into 100’s of lines of styling-and-business-logic entangled spaghetti code.

    However, I agree that the hierarchical data is easier to work with when using XSLT’s. Take a look at these 2 examples, the first one is a left menu written in XSLT, the last one does the exact same thing, but it is written in C#:

    http://briancaos.wordpress.com/2009/04/24/creating-a-tree-like-left-menu-in-sitecore-using-xslt/

    http://briancaos.wordpress.com/2011/08/03/creating-a-tree-like-left-menu-in-sitecore-using-usercontrols-and-c/

    But even if the left menu is easier to understand, I would not recommend using XSLT’s in Sitecore projects anymore, as most tasks is just as easy to do in C# and User Controls:

    http://briancaos.wordpress.com/2011/01/25/using-usercontrols-instead-of-xslt-in-sitecore-projects/

    • Alistair Deneys says:

      As I said Brian, the right tool for the right job. And you nailed the issue in your comment, style-and-business-logic spaghetti code. I think with a good design, XSLTs can be maintainable and very easily extensible. Don’t place the blame for people’s misuse of a technology on the technology. Same arguments go for ASP.NET and WebForms 🙂 .

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Categories

The views expressed on this blog are solely my own and do not necessarily reflect the views of my employer.
%d bloggers like this: