Extend the Microsoft XSLT processor with JavaScript

Microsoft's XSLT processor provides some powerful functionality, such as data type conversion and arithmetic and string manipulation. See how you can extend this functionality even further with JavaScript.

Microsoft’s XSLT processor (MSXML) provides a number of built-in functions that cover a range of categories. These categories include:
  • Data type conversion
  • Arithmetic, string manipulation
  • Aggregation
  • XML document-related functions

In addition, you can extend this functionality by writing your own methods in Microsoft’s version of JavaScript, JScript. Unfortunately, a number of issues stand between you and the execution of your extension functions, such as the different versions of MSXML. You also need to hide JavaScript from the XML parser so that greater-than and less-than comparisons can be performed and the XSLT is still well formed. And then there's the fact that nobody has really bothered to document how to write extension functions.

Internet Explorer 5.0 and later support MSXML, but by default, that support is for version 2.0—and the current version of MSXML is 4.0. Version 2.0 of MSXML supports only the working draft of XSLT. This means you must force users to download MSXML 4.0 from Microsoft, write your code using a working draft version of XSLT that has less functionality, or perform the transformation on the server side.

Unless you are developing an intranet application, where you can have total control of the environment, the first alternative isn’t an option. And because MSXML 4.0 doesn’t support the working draft of XSLT, the second approach would require two XSLT documents: one for version 2.0 and another for versions 3.0 and 4.0. The final choice is viable only for certain types of transformations performed on the server side. For example, transforming an ADO recordset into an HTML select can be accomplished on the server side where the recordset resides.

Although the process of writing extension functions hasn't been well documented, I was able to figure it out through trial and error. Here's a look at the function I wrote and some of the problems I ran into along the way.

Extend MSXML for speed
My reasons for extending the MSXML functions can be boiled down to one word: speed. I have a number of large XHTML select objects that are interdependent. If a particular division is selected, the available products are limited to that division. In addition, the selected products determine which models are available. Rather than query the database each time a division or product changed, I decided to build an XHTML select object for every product and model stored in their respective tables, and later show only the appropriate information.

Determining the relationship between division, product, and model is accomplished through the use of the option tags value and id attributes. The value attributes from the division select object correspond to the id attributes from the product select object. And the value attributes correspond to the id attributes from the model select object. This allows for the options to be dynamically altered based upon what has been selected.

Namespaces needed for extension functions
All XML-based documents use namespaces to identify elements and attributes from different vocabularies and to group these vocabularies to facilitate software recognition. Every time a new vocabulary is added, a namespace is added for that vocabulary. This means that two new namespaces are needed to add extension functions. The first namespace is for the scripting vocabulary, and the second is for the vocabulary with your extension functions. This results in the XSLT document root node, as shown in Listing A.

Extension functions are invoked from within the XPath expression. Let’s say that you call your function limit, and it returns either true or false based upon two string arguments. The first argument is a single attribute, while the second argument is a parameter consisting of acceptable values for the first argument separated by commas. Using the namespaces from Listing A, the resulting XSLT element would look like that in Listing B.

When the extension function limit is invoked, binding takes place. Binding is when Microsoft’s XSLT processor determines which function to invoke. The MSXML processor attempts to find an msxsl:script tag with an implements-prefix attribute that matches your function’s namespace. Once binding has been accomplished, your extension function is invoked.

At this point, I was reminded of two things about XSL documents. Since XSL documents are XML documents, they must be well formed, meaning that every tag is a container tag. So don’t leave any greater than (>) or less than (<) signs lying around. I was also reminded that elements and attributes are of the node-set data type, not the string data type.

Resolving the first error
The first error was a mindset error. I was thinking in JavaScript, not XML. Placing the JavaScript function in a CDATA section resolved this problem, and the document was well formed. A CDATA section, which starts with <![CDATA[ and ends with ]]>, instructs the XML parser that the enclosed is raw character data and not to treat it as markup.

Resolving the second error
The second error was a run-time error. I resolved it by changing the @id to string(@id), which passed a string instead of a node set to the function limit. Listing C shows the final XSLT document, limit.xsl, and Listing D shows the JavaScript function that uses it.


Extending MSXML's built-in functionality by writing your own methods can be a little tricky here and there. But now that you've seen how I resolved the issues that stood between me and the execution of my extension function, they shouldn't be a problem for you.

Editor's Picks