Refactoring with XSLT and JavaScript

Refactoring a Web-based order entry page with 3,000 lines of VBScript and HTML wasn't easy. But with a bit of XML, XSLT, and a liberal dose of JavaScript, our author got the page down to half its size.

A while back I wrote an article on how to extend XSLT through the use of JavaScript. Recently I found myself revisiting this subject due to an unusually pesky problem—I was assigned the task of rewriting the order summary page for the company's Web-based order entry system. It wasn't that there was anything specifically wrong with the page; it's just that several people using vastly different styles wrote it.

This particular monster was 3,000 lines of VBScript and HTML, not counting includes. There were nested loops; arrays, stored procedures and embedded SQL pulling related data from seven different tables. It was basically a collection of bad habits from multiple developers aged to perfection. This page wasn’t designed, it just happened. A rewrite was desperately needed to improve manageability before any other changes could be considered.

The new page was a complete rewrite of the original Web page, faster than the original, cleaner than the original, less than half the size of the original. This miracle was accomplished through the use of XML, XSLT, and liberal sprinklings of JavaScript.

XML, XSLT, and JavaScript, oh my!
Because ADO recordsets can be saved as XML, initially creating the XML is not only easier than the old method of building arrays, it’s faster. However, problems arose due to the relationships of the various tables. For example, comment table rows can exist for both the order header and the order line items. Unfortunately, merging XML documents using XSLT isn't always a simple matter.

Existing merging methods
The first, and easiest, method to merge XML documents using XSL is to "concatenate" the documents programmatically, as shown in Listing A. A root node is first created, and then clones of the two documents are attached as children of that root. XSLT is then used to rearrange the nodes to the desired layout. Listing B shows an example of how this method of merging is accomplished.

The other accepted methods of merging XML documents is to use either the xsl:import tag or the document() function. The problem with these methods is that because of the way the tag and the function works one of the XML documents needs to be written to a temporary file, which is then read as input. In my opinion, anytime that a file is written to the server there's a lot of overhead. Also, security is another potential issue due to having folder write permission enabled.

Using JavaScript extensions
Because of the number of XML documents that needed merging and my religious reasons against the use of temporary files on a Web server, I decided to try a different method of merging XML documents. Instead of the concatenate method shown above, a single XML document would serve as the starting point for the merge. Each of the other XML documents would be passed as parameters to the XSLT since XML is essentially text-based.

When using XSLT, the string representations of an XML document are really pretty useless; other than a few string functions, there isn’t much that can be done with it. However, a JavaScript extension function provides the ability to "reconstitute" the string into an XML document. This document can then be parsed programmatically to return a document fragment consisting of the desired node or nodes. Below are examples of two XML documents to be merged:


Listing C contains the XSLT, and Listing D shows how the XSLT is applied in ASP.

The extension function getNode() accepts two or more parameters; the first parameter is the string representation of the XML document, while the other parameters are the nodes' tags to return in the document fragment. The result is shown in Listing E.

More nodes
The getNode() function helped a lot in increasing the manageability of the “Monster”. It would have fit in all situations, but customers are an unreasonable group—they have a tendency to place orders for more than one item at a time. Customers really don’t have any respect for elegant code; they don’t care about elegant code; and they would rather have code that works in all situations. Customers really have some nerve!

What I needed was a way to merge XML documents on a row-by-row basis. The result was the mergeDocuments() extension function shown in Listing F. The mergeDocuments() function accepts three parameters. The first two parameters are the string representations of XML documents to be merged; and the third is the node to compare. The first document serves as the primary document and is always returned by the function. The second document is then matched to the first document based upon the node name supplied in the third parameter. All of the sibling nodes in the second document are cloned and attached to the parent node in the first document.

Using the XML documents in Listing G, see the results in Listing H.

One of the positive things about being able to rewrite a Web page that nobody wants to touch is that the only thing that really matters is the end result. This means that the choice of tools is up to the developer; in my case, this meant using XML and XSLT. Of course this freedom means that the possibility of creating another monstrous page can be an issue, but while XSLT and extensions aren't silver bullets, they can be used to slay the occasional monster.


Editor's Picks