Xslt 1.0 biggest issues (kind of) solved

Xslt 1.0 has a number of issues that can make the life of an Xml developer frustrating. A lot of them are addressed by Xslt 2.0. Unfortunately the .NET Framework does not have an Xslt 2.0 compliant processor. Fortunately most of the biggest Xslt 1.0 pain points have workarounds. Having workarounds rather than real solutions is almost never ideal but almost always better than nothing.

Grouping

When working with Xml documents it is common to have a list of elements that contain an attribute whose value can be used to qualify a given element to a group. As opposed to Xslt 2.0 which contains the xsl:for-each-group instruction Xslt 1.0 does not natively support grouping. It’s probably one of the biggest (if not the biggest) weaknesses of Xslt 1.0. Although not that straightforward it is still possible to group items in Xslt 1.0. There are basically two techniques for doing this:

  • Simple grouping ( *not really recommended* )
  • so-called Muenchian grouping

Simple grouping is just looking for keys using preceding-sibling (or following-sibling) axis (something like: item[not(preceding-sibling::item/@group = @group)]). While it may work fine for small sets its performance is horrible and therefore using this method is not recommended. Instead you should group your items using so-called Muenchian grouping which is based on using keys and is way faster than simple grouping (also more complicated). You can find more details on Muenchian grouping in Wikipedia: https://en.wikipedia.org/wiki/Muenchian_grouping. The article also contains a link to in-depth explanation of how Muenchian grouping works.

Regular expressions

It’s not easy to work with strings in Xslt 1.0 let alone regular expressions. A lot of new string functions were introduced in Xslt 2.0. In addition a new instruction xsl:analyze-string enables using regular expressions. While most of the new string functions can be emulated in Xslt 1.0 using existing functions or (recursive) templates this is not the case with regular expressions. In this case the workaround is to use either scripts or extension objects in which it is possible to use regular expressions from .NET Framework. An example that uses an embedded script is shown below:

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

<xsl:stylesheet version="1.0" xmlns:xsl="https://www.w3.org/1999/XSL/Transform"

    xmlns:msxsl="urn:schemas-microsoft-com:xslt"

    xmlns:my-scripts="urn:my-scripts"

    exclude-result-prefixes="msxsl">

 

  <xsl:output method="text"/>

  <msxsl:script language="C#" implements-prefix="my-scripts">

    <![CDATA[

    public bool ValidateEmailAddress(string emailAddress)

    {

      return Regex.Match(emailAddress, @"^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,4}$").Success;

    }

    ]]>

  </msxsl:script>

 

  <xsl:template match="/">

    <xsl:variable name="addresses">

      <email>john@contoso.com</email>

      <email>john@contoso</email>

    </xsl:variable>

    <xsl:for-each select="msxsl:node-set($addresses)/*">

      Address <xsl:value-of select="." /> is <xsl:if test="not(my-scripts:ValidateEmailAddress(.))">not </xsl:if>valid.

    </xsl:for-each>

  </xsl:template>

 

</xsl:stylesheet>

XHTML output method

In addition to Xml, Html and Text output methods available in Xslt 1.0 Xslt 2.0 introduces a new output method which is XHTML. Fortunately XslCompiledTransform allows for providing your own XmlWriter that will be used to write transformation results. If you provide a specialized version of XmlWriter that can write XHTML compliant documents you will be able to achieve the same functionality as the one enabled by Xslt 2.0. If you don’t have such a writer handy you can use the one that is part of Mvp.Xml library (https://mvpxml.codeplex.com/). See Oleg Tkachenko’s blog entry for some additional details https://www.tkachenko.com/blog/archives/000712.htm

DateTime support

There is no DateTime type defined in XPath 1.0. As a result working with date time in Xslt 1.0 is not easy. Xslt 2.0 on the other hand adopted uses XPath 2.0 type system. As a result DateTime type is first class citizen in Xslt 2.0. How to go about DateTime type in Xslt 1.0? Here are a few ideas you may try depending on your needs:

  • Consider using ISO 8601 date format – it is easy to sort, compare and get date parts
  • Check the built-in extension functions for formatting date and time (https://msdn.microsoft.com/en-us/library/ms256453.aspx)
  • If none of the above works you may need to use scripts or extension objects

Techniques for emulating Xslt 2.0 features with Xslt 1.0 processor can be summarized as follows:

Problem

Xslt 2.0 feature

How to achieve this in Xslt 1.0

Grouping

xsl:for-each-group instruction

Muenchian grouping

Simple grouping (small sets only)

Regular expressions

xsl:analyze-string instruction

Embedded scripts/extension objects

Xhtml output

New xhtml output method

Custom XmlWriter using xhtml rules to output transformation results

Support for processing dates

DateTime type

Using ISO-8601 date format

Built-in extension functions

Scripts/extension objects

Some of the workarounds I provided may require using scripts. If you are concerned about portability (using scripts will make your Xslt stylesheet not portable) you may want to use EXSLT (https://exslt.org) – a set of well-defined extensions for Xslt 1.0. XslCompiledTransform supports natively only two functions from the EXSLT set (exsl:node-set() and exsl:object-type()) but you can use EXSLT.NET module from the Mvp.Xml project (https://mvpxml.codeplex.com/wikipage?title=EXSLT.NET&referringTitle=Home). Since EXSLT is supported on quite a few platforms you should be able to create portable stylesheets.

Pawel Kadluczka