Tuesday 29 May 2007

The EXSLT node-set function

Following on from a thread on xsl-list which turned to the question of support for the useful xx:node-set() extension function in the XSLT engines used by popular browsers.

Using XPath extension functions in a cross-browser environment is greatly simplified if all the XSLT engines use the same extension namespace. This was one of the motivating reasons behind the community initiative to standardise on the EXSLT extension namespaces.

Opera's XSLT engine supports exslt:node-set, Mozilla/Firefox's XSLT engine doesn't in the current release, but does in the "Gran Paradiso" alpha tests for Firefox 3. Internet Explorer (6 and 7) don't support EXSLT, but do support the functionally identical msxsl:node-set function in the usual msxsl extension namespace.

The usual way to handle exslt:node-set and msxsl:node-set in the same document is to use xsl:choose blocks, with tests on function-available('exslt:node-set') but that is often inconvenient if you want to use xx:node-set in the middle of an XPath.

In the above XSL-List thread I casually suggested that an alternative would be to just always use exslt:node-set in the body of the stylesheet and use the msxsl:script extension to define exslt:node-set for IE. That turned out not to be as easy as I thought as node-set isn't a valid function name in either of the supported extension languages in msxsl (JScript or VBScript). However Julian Reschke came up with the construct needed, use associative array syntax so you can use ['node-set'] to define the function. A complete stylesheet using this technique is shown below

<xsl:stylesheet
  version="1.0" 
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  xmlns:exslt="http://exslt.org/common"
  xmlns:msxsl="urn:schemas-microsoft-com:xslt"
  exclude-result-prefixes="exslt msxsl">
  

<msxsl:script language="JScript" implements-prefix="exslt">
 this['node-set'] =  function (x) {
  return x;
  }
</msxsl:script>


<xsl:variable name="x">
  <y/>
</xsl:variable>

<xsl:template match="x">
  <html>
    <head><title>test exslt node set</title></head>
    <body>
      <xsl:apply-templates select="exslt:node-set($x)/*"/>
    </body>
  </html>
</xsl:template>

<xsl:template match="y">
  <p>node set!</p>
</xsl:template>

</xsl:stylesheet>

The same stylesheet is online together with a test file which just consists of a stylesheet reference and an empty element <x/>.

If your browser supports EXSLT (either natively or using the technique above) viewing the test file link should show "node set!". This appears to work in Firefox Gran paradiso, Internet Explorer 7, Opera 9.21. It doesn't work in Firefox 2 (you get an error message about an unsupported extension function). Users of other browsers that support XSLT (Safari?) feel free to report any results in the comments!

Updated 2008-08-06 to move test file to a different server.

Updated 2009-12-17 to move to google code server. The file now reports that Safari 3.2 supports node-set

Wednesday 9 May 2007

schematron updates

Ken Holman posted a problem with the skeleton which also applied to my version described in an earlier post so I updated the code, at the same URI. There have also been some updates in the last few days removing saxon dependencies, as described in the coments, thanks to Colin Adams for picking those up. [updated again 2007/05/09] A couple of errors had crept into the schematron-get-full-path mode used to display the XPath of the current node.