Menu

Sarissa to the Rescue

February 23, 2005

Emmanouil Batsis

Client-side XML processing. Today's browsers do cover the basics and some of them go even further, offering support for XHTML, SVG, XSLT, XPath , XLink, validation using W3C XML Schema, and more. This article will introduce you to basic cross-browser XML development with the aid of Sarissa, an ECMAScript library designed to stop those nasty incompatibilities before they get too close.

Getting Started

Using XML on the client enables you to do things you've never done before, especially when it comes to control of structured information and delivering an enhanced user experience. Let's go over some typical examples.

Your favorite designer could very well be hiding this from you: it is possible to update only parts of a web page with data coming from a request to your server without refreshing the page and without scripting between those troublesome iframe elements. The request can be the result of user interaction handled by your script. Using the XMLHttpRequest object, you can perform requests over HTTP and obtain the XML response as a DOM-compatible object. You can then process that object further, if you want, before finally injecting it into the document using plain DOM methods, adding to your usability and saving bandwidth.

Use of XML on the client side is often driven by server-side requirements. For example, a high number of concurrent requests involving XSLT-based transformations sounds like trouble for any server. A transformation requires three tree structures in memory, one for each of the source, transform, and result documents. Outsourcing the transformation process to capable clients is a little like outsourcing the process of furniture assembly to the clients themselves. They get what they want more quickly and pay less while you save resources without losing the sale. After the assembly, clients keep the screwdriver for future use much the same way a browser will cache your XSLT document.

Another use case may involve sending structured information to the server application. To perform this, you can create a DOM document programmatically (even by parsing an XML string), perhaps processing it further, and finally submit it to the server.

You get the idea. This can go on to complex applications with rich UIs, for example a web-based XML editor based on XSLT, DOM, and CSS.

But let's go over the basics first.

Basic Training

The real issue in client-side XML development is browser incompatibility around implementations and extensions of the W3C DOM. The Sarissa library hides these incompatibilities for you, also adding some useful utilities into the mix.

A typical script block dealing with XML starts with instantiating a DOM document. With Sarissa, getting a new XMLDocument object is done by calling a 'static' method:

// Get a browser specific DOM Document object

var oDomDoc = Sarissa.getDomDocument();

// more DOM code here

In standards-based browsers, this block is equal to document.implementation.createDocument. In IE, Sarissa just uses the most recent MSXML ProgId to construct an ActiveX-based object as appropriate. Additionally, you can pass two string parameters to that factory method, which correspond to a namespace URI and a local name respectively. Those are used by Sarissa to create a root element and add it to the newly constructed XMLDocument:

// construct a document containing

// <foo xmlns="http://myserver/ns/uri" />

var oDomDoc = 

Sarissa.getDomDocument("http://myserver/ns/uri","foo");

You can also populate the Document using an XML string. The above line is equal to

var oDomDoc = 

Sarissa.getDomDocument("http://myserver/ns/uri","foo");

// populate the DOM Document using an XML string

oDomDoc.loadXML("<foo xmlns='http://myserver/ns/uri' />");

How about loading an XML document from a URL? Just copy the above XML, paste it in a new file on your server and load it like this:

var oDomDoc = Sarissa.getDomDocument("http://myserver/ns/uri","foo");

// set loading method to synchronous

oDomDoc.async = false;

// populate the DOM Document using a remote file

oDomDoc.load("path/to/my/file.xml");

// report any XML parsing errors

if(oDomDoc.parseError != 0){

   // construct a human readable

   // error description

   alert(Sarissa.getParseErrorText(oDomDoc););

}else{

   // show loaded XML

   alert(Sarissa.serialize(oDomDoc););

};

We first load the remote file using the load method of a XMLDocument using synchronous loading, meaning that the if branch will only be executed after the load method returns. Then we check for a parsing error. If an error exists, the user sees the result of a call to Sarissa.getParseErrorText, which provides a string with a human-readable description of the error. If there is no error, the user sees the XML string serialization of the document returned from Sarissa.serialize. This is like IE's xml property of DOM Nodes, with the difference being that it works for everyone.

More Tricks

The XMLHttpRequest object, available by one name or another in every major browser by now, is used when you simply need more control over the request to the remote server, like specifying the HTTP method and headers. You can use it to load the same XML file as above like:

var xmlHttp = new XMLHttpRequest();

// specify HTTP method, file URL and 

// whether to use asynchronous loading

xmlHttp.open("GET", "path/to/my/file.xml", false);

// perform the actual request

xmlHttp.send(null);

// show result

alert(Sarissa.serialize(xmlHttp.responseXML));

What we've done here is create a new XMLHttpRequest object and configure it to request the specified URL using HTTP GET asynchronously. We then perform the actual request and when that returns, we serialize the response XML which is available via the responseXML property.

To perform XSLT transformations, two XMLDocument objects are needed, one for the XSLT transform and one for the source document. Supposing we have obtained those as xslDoc and xmlDoc respectively, we can perform the transformation using an XSLTProcessor:

// create an instance of XSLTProcessor

var processor = new XSLTProcessor();

// configure the processor to use our stylesheet 

processor.importStylesheet(xslDoc);

// transform and store the result as a new doc

var resultDocument = 

processor.transformToDocument(xmlDoc);

// show transformation results

alert(Sarissa.serialize(resultDocument));

Here we create a new processor and load our stylesheet to it using the importStylesheet method. It is worth noting that a single configured instance of XSLTProcessor can be re-used to transform more than one source document. We don't have to load the stylesheet each time. Then we store the transformation result into a new XMLDocument object and display its serialization to the user.

You may be aware that IE has added the transformNode and transformNodeToObject methods in its implementation of the XMLDocument object. Sarissa does implement those methods for Mozilla but they are deprecated. The use of the XSLTProcessor is recommended as it provides a more efficient way to transform multiple documents and set XSLT parameters. A last word on XSLT -- Right now XSLT and XPath stuff are not supported for Konqueror and Safari, although this is expected to change.

Injections

Playing with XML programmatically is cool, but we usually want to modify our page using the resulting markup. Suppose we want to inject an XML node bound as fooNode in our document as a child of an element with an id value of 'targetNode':


document.getElementById('targetNode').appendChild(document.importNode(fooNode, true));

It is possible to get into trouble with this code. Although it's the most efficient way to append the node in the document, it could result to an error if, for example, the node you are trying to append is a document node. Serializing with Sarissa.serialize and setting the innerHTML of the target element is always an option, but I would suggest doing it properly using DOM instead.

The Final Touch

So all of this is great but it's still too much code to write, and probably too error-prone, especially if you are a code completion wimp like me. Moreover, the case becomes worse if you want to use XSLT on the client where applicable. To do that, you need to work on both end points:

  • your server must be able to transform the XML before sending it, or leave the transformation to the client. This can be dependent on the URL requested or an HTTP parameter.

  • The client must know if it is able to perform the transformation and ask the XML as appropriate. IS_ENABLED_XSLTPROC is a Boolean constant you can check in your logic to figure out what to do.

With these two issues addressed, you could result in something like

// set an HTTP parameter depending on whether you want 

// the transformation on the server or not

var clientTransform = Sarissa.IS_ENABLED_XSLTPROC;

// now construct the URL as appropriate

var url = 'path/to/file?sent-as-is=' + clientTransforml;

So we have constructed the URL we want thanks to a Sarissa constant that tells us whether our client is able to use a transformer. Now, supposing we still want to append the result targetElement as with our previous example and with an instance of XSLTProcessor at hand (which may be null), we perform this with just one line:

Sarissa.updateContentFromURI(url, targetElement, processor);

This will work if the URL does point to an HTTP server. If you cannot have access to one, just use your filesystem instead; you will need to load or build the source document manually and call Sarissa.updateContentFromNode with it.

Client-side XML can open new doors for your applications. Using Sarissa, it can be easy as well. Sarissa includes a lot more and the code can even guide you in writing your own reusable components. Give it a try and let me know what you are up to. Maybe next time I'll show you how to build a browser-based XML editor.

Resources: