lthough you may not be able to switch to ASP.NET at your location, you can take full advantage of XML and XSLT transforms now to make your site more flexible, more robust, and in some cases, faster. In this article, you’ll see how to use XML and XSLT to display, manage, and cache data in your ASP applications.
Suppose you have a list of internal links for your site?the basis of a menu. You want to format the links down the left side of the screen as a list in a table. In other words, the HTML you need?in very plain form?looks something like this:
Link 1 Link 2 Link 3
The HTML above describes two tables that appear next to one another. The one on the left holds the list of links, while the one on the right holds the main site content.
Transform XML with XSLT Stylesheets
As the list of links won’t change terribly often, you could easily store them in a simple XML document. For example:
You can write an XSLT stylesheet to transform that data into HTML, for example:
transform on the server:
<%@ Language=VBScript %> <% dim xml dim xsl set xml = Server.CreateObject("MSXML2.DOMDocument.4.0") set xsl = Server.CreateObject("MSXML2.DOMDocument.4.0") xml.load Server.MapPath(".") & "menu.xml" xsl.load Server.MapPath(".") & "menu.xsl" %> <%Response.Write xml.transformNode(xsl)%>
|Author’s Note: For the code in this article, I’ve used the MSXML version 4 processor, but the code works fine with version 3. To use it with version 3, change every occurrence of Server.CreateObject(“someobjectProgID.4.0”) to Server.CreateObject(“someobjectProgID.3.0”).|
Add Server-Side Caching for Speed
The simple process you’ve seen loads the XML document and XSLT stylesheets for each request. It works fineand it’s faster than retrieving the list of links from a database and then formatting it with VBScript code on the server. But there’s a problem with this methodit’s not fast enough, and you can optimize it. To do so, use the free-threaded version of the DOMDocument object to cache the XML document containing the links and the XSLT file containing the stylesheet in memory. By doing that, you avoid having to create DOMDocument objects and read the document from disk each time. Although you would normally load these objects in your global.asa file, you can just as easily load them during any page request. For example:
<%@ Language=VBScript %> <% dim xml dim xsl dim LinkData_filename dim appPath appPath = Server.MapPath(".") LinkData_filename = appPath & "menu.xml" if isEmpty(Application("LinkData")) or _ Request("refresh") = "true" then Response.Write "Loading from disk" ' load the XML document set xml = Server.CreateObject _ ("MSXML2.FreeThreadedDOMDocument.4.0") call loadXMLFile(xml, LinkData_filename) call updateAppVariable("LinkData", xml) else Response.Write "Loading from cache" set xml = Application("LinkData") end ifThe first time a client requests this page, the server creates and stores a FreeThreadedDOMDocument object at Application scope, where it’s available for each subsequent request. You can see the difference in performance between the initial load and the cached copy by requesting the page twice (see the file testMenu2.asp in the downloadable code for the example. For the purposes of this example, I’ve made it write messages to the browser. The first time you load the page, it prints “Loading from disk,” but all subsequent times, it prints “Loading from cache.”
The loadXMLFile() function is a wrapper that displays error information if the XML file fails to load properly.
sub loadXMLFile(xmldoc, filename) if not xmldoc.load(filename) then Response.Write “Error loading the file ‘” _ & filename & “‘.When you update values stored in the Application object, you need to lock the Application to avoid the problem of one thread attempting to retrieve the data while another thread is changing it. Programmers sometimes forget to lock the Application object. The updateAppVariable function protects and generalizes setting Application variables:
” Response.Write “Description: ” & _ xmldoc.parseError.reason & “
” Response.Write “Line: ” & _ xmldoc.parseError.line & “
” Response.Write “Line position: ” _ & xmldoc.parseError.linepos & “
” Response.End end if end sub
sub updateAppVariable(varName, value) Application.Lock if isObject(value) then set Application(varname) = value else Application(varname) = value end if Application.UnLock end subYou use a slightly different sequence to cache XSLT stylesheets. The XSLTemplate object has a stylesheet property that accepts a FreeThreadedDOMDocument object. You create the XSLTemplate object, set its stylesheet property, and then cache the XSLTemplate at Application scope rather than the FreeThreadedDOMDocument itself. The process of loading and caching the XSLTemplate object is very similar to storing a FreeThreadedDOMDocument alone. For example, the following code loads the XSLT file that creates the list of links:
‘ load the list XSL file set xsl = Server.CreateObject _ (“MSXML2.FreeThreadedDOMDocument.4.0”) call loadXMLFile(xsl, LinkSelect_filename) set template = Server.CreateObject _ (“MSXML2.XSLTemplate.4.0”) set template.stylesheet = xsl call updateAppVariable(“LinkSelect”, template)After creating the XSLTemplate object, you use its createProcessor method to create an XSLProcessor instance that actually performs the transformation. You set the XSLProcessor’s input property to the XML file and retrieve the results via the output property.
set template = Application(“LinkSelect”) set proc = template.createProcessor proc.input = xml Response.Write proc.outputThe output is a string containing the transformed XML data. XSLTemplates have a speed advantage over caching the stylesheet directly as a FreeThreadedDOMDocument object because the stylesheet is pre-compiled, rather than compiled for each use. In addition, the XSLTemplate objects make using XSLT parameters very easy, as you’ll see later in this article.
Add XML/XSLT-Based Administration
Creating the links table using cached data is measurably faster, but it has a problem. By loading the XML document from memory each time, you could change the link file and propagate the change to clients immediately. But by pulling the datafrom cache, changes you make to the file will not appear until you shut the application down and restart it. In other words, you need a way to know when to refresh the cached data. You can do that in several ways:
- by checking the file date/time against a stored file date/time every time a page requests the file
- by creating a method that checks a Request parameter, for example “RefreshMenu=true”
- by creating an ASP page that lets the appropriate people update the fileand then automatically refreshes the data.
The people who maintain the list of links need to be able to add, modify, and delete tags within the XML file. XSLT isn’t strictly required to build and display the user interface for list administration, but it simplifies and optimizes the code considerably, offloading otherwise messy and slow string operations in your VBScript code to the XSLT stylesheet instead.
Because the fields to add a new link are identical to the fields to modify an existing link you can use the same user interface for both operations. Therefore you only need two transformsone to display the list and one to display the data for editing, along with the appropriate controls. The following stylesheet displays the link list in alphabetical order by title. Note that you might extend this stylesheet to let users sort the links in other ways as well, for example, to display the links in the order they appear on the site (the code is in the file menuEditSelect.xsl).