January 24, 2006

exsl:object-type() XSLT extension function in .NET 2.0

Now that XslCompiledTransform in .NET 2.0 supports exsl:object-type() extension function I think a little intro is needed as this is really new function for Microsoft-oriented XSLT developers. ...

exsl:object-type() function brings a bit of reflection functionality into XSLT allowing dynamic type identification at runtime. That is using exsl:object-type() one can determine type of an object, e.g. a type of passed parameter value. In XSLT 1.0 type system that means 'string', 'number', 'boolean', 'node-set', 'RTF' or 'external':

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:exsl="http://exslt.org/common" exclude-result-prefixes="exsl"> <xsl:output indent="yes" omit-xml-declaration="yes"/> <xsl:template match="/"> <out> <xsl:variable name="node" select="/data"/> <xsl:variable name="string" select="'string'"/> <xsl:variable name="number" select="11"/> <xsl:variable name="rtf"> <foo/> <bar/> </xsl:variable> <xsl:variable name="boolean" select="true()"/> Node type: <xsl:value-of select="exsl:object-type($node)"/> String type: <xsl:value-of select="exsl:object-type($string)"/> Boolean type: <xsl:value-of select="exsl:object-type($boolean)"/> Number type: <xsl:value-of select="exsl:object-type($number)"/> RTF type: <xsl:value-of select="exsl:object-type($rtf)"/> </out> </xsl:template> </xsl:stylesheet> The result is: <out> Node type: node-set String type: string Boolean type: boolean Number type: number RTF type: RTF</out> Provided weakly typed nature of XSLT 1.0 this function can become really useful for employing defensive programming (e.g. to assert that a parameter passed to a named template contains actually a nodeset), testing or even debugging.

For a sample of defensive programming using exsl:object-type() function consider the following dummy stylesheet:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:exsl="http://exslt.org/common" exclude-result-prefixes="exsl"> <xsl:template match="/"> <out> <xsl:variable name="tmp"> <foo/><bar/> </xsl:variable> <xsl:call-template name="util42"> <xsl:with-param name="nodes" select="$tmp"/> </xsl:call-template> </out> </xsl:template> <xsl:template name="util42"> <xsl:param name="nodes"/> <xsl:if test="exsl:object-type($nodes) != 'node-set'"> <xsl:message terminate="yes"> util42 template expects parameter $nodes to be a nodeset, not '<xsl:value-of select="exsl:object-type($nodes)"/>'!</xsl:message> </xsl:if> <!-- do some stuff --> <xsl:apply-templates select="$nodes"/> </xsl:template> </xsl:stylesheet> Here util42 template before doing any work asserts than actual parameter value passed is a nodeset, not anything else. If you comment out that <xsl:if> test, you'll get the following transformation error:
XslTransformException
---------------------
To use a result tree fragment in a path expression, first convert it to a node-set 
using the msxsl:node-set() function.
Well, as usual with automatic error messages - at least unclear. Now with object type check you should get this:
XslTransformException
---------------------
util42 template expects parameter $nodes to be a nodeset, not 'RTF'!
Definitely more meaningful and safe. And of course it's now up to template author whether to terminate transformation or to recover.

Pretty useful fiunction. And when it comes to QA - that's a godsend. As a matter of interest, AFAIK exsl:object-type() function was implemented to help Microsoft XML Team with XslCompiledTransform testing in the first place.