Out of the Angle Brackets
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 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: http://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="http://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 (http://mvpxml.codeplex.com/). See Oleg Tkachenko’s blog entry for some additional details http://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:
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
xsl:for-each-group instruction
Muenchian grouping
Simple grouping (small sets only)
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 (http://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 (http://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
Yea, I think I'll just use Saxon for XSLT work.
http://saxon.sourceforge.net/
Or use XQSharp which is a native .Net XSLT 2.0 processor:
http://www.xqsharp.com
You guys have really dropped the ball with XSLT 2.0.
Thanks!
As you said, having workarounds rather than real solutions is almost never ideal, however. Maybe you could support XSLT 2.0 in the not-too-distant future?
When can we expect XLST 2.0 support in core framework of .Net ? Its been a long time that XLST 2.0 is released. Hopefully it will be in place soon.