Sarissa to the Rescue
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 and
xslDoc respectively, we can perform the
transformation using an xmlDoc:
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_XSLTPROCis 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:
|
- XMLHttpRequest
2005-02-24 22:29:56 shmert - XMLHttpRequest
2005-02-25 01:46:37 bryan rasmussen