Thursday, February 07, 2008

Scripts, Extensions and XSLT

XSLT is about processing XML in which data is stored as text. It'd be really useful to be able to use regular expressions within the XSLT to process this text. We could reformat date strings, for example. XSLT 2 supports regular expressions natively, but unfortunately .NET 2.0 does not seem to support this. This caused me to investigate ways of incorporating scripting into XSLT with .NET. I've found 2:


1) The first way is to embed a script within the XSL document within msxsl:script tags.

You add the msxsl namespace reference to the stylesheet declaration (xmlns:msxsl="urn:schemas-microsoft-com:xslt") and provide a custom namespace prefix for your scripts (e.g. xmlns:MyScripts="urn:MyNamespace").

i.e. -


<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:msxsl="urn:schemas-microsoft-com:xslt"
xmlns:MyScripts="urn:MyNamespace">


Then you declare your script somewhere after this, setting the "implements-prefix" attribute to the custom namespace prefix you declared previously:

e.g.

<msxsl:script language="C#" implements-prefix="MyScripts">
<![CDATA[
public string UpperCase(string input){
// do something with input
return input.ToUpper();
}
]]>
</msxsl:script>


You can then use this script within your XSLT.

e.g.

<xsl:value-of select='MyScripts:UpperCase(@thing)'>


However, when load the xslt into the XsltCompiledTransform, you must pass in an XsltSettings object with the "EnableScript" property set to true, else it'll complain at runtime.


2) The second way involves making a class in your .NET language of choice (mine being C#) and defining the script function there.

e.g.


public class XsltScripts
{
public String UpperCase(String input)
{
return input.ToUpper();
}
}


You then declare a custom namespace and prefix for your scripts as before, and pass in an XsltArgument object that contains a reference to your C# object and the namespace when you call the transform.

e.g.

XsltArgumentList args = new XsltArgumentList();
args.AddExtensionObject("urn:MyNamespace", new XsltScripts());

xslt.Transform(..., _xsltArgs, ...);


You can then reference the code from the XSLT exactly the same way as we did with the previous embedded script method.

e.g.

<xsl:value-of select='MyScripts:UpperCase(@thing)'>


Of course, your code can do much more useful stuff, like using regular expressions on the input string.