Processing XML


To round off this discussion on XML technology, it is worth taking a brief look at some of the means of processing the XML documents and schemas that we have so far examined to see how we traverse from the XML level to the application level. There are a number of standard, cross-platform tools available that perform much of the hard work involved in processing XML. In this section we concentrate on three of the most prevalent XML processing technologies: SAX, DOM, and XSLT.

The examples we have chosen to illustrate the technologies are necessarily simple. In each example we simply harvest the character information from a simple DVD document as shown Figure 2-55.

With each XML processing tool, we take the XML shown in Figure 2-55 and present it as an XML fragment such as:

 <d:character xmlns:d="http://dvd.example.org">         Qui Gon Jin     </d:character>     <d:character xmlns:d="http://dvd.example.org">         Queen Amidala     </d:character>     <d:character xmlns:d="http://dvd.example.org">         Obi Wan Kenobi     </d:character>     <d:character xmlns:d="http://dvd.example.org">         Anakin Skywalker     </d:character>     <d:character xmlns:d="http://dvd.example.org">         Senator Palpatine     </d:character> 

which could then be used as the basis for other processing.

Figure 2-55. A complex XML document.
 <?xml version="1.0" encoding="utf-8"?> <d:dvd xmlns:d="http://dvd.example.org" region="2">   <d:title>The Phantom Menace</d:title>   <d:year>2001</d:year>   <d:language>     <d:audio>English</d:audio>     <d:subtitle>Danish</d:subtitle>     <d:subtitle>Norwegian</d:subtitle>     <d:subtitle>Swedish</d:subtitle>     <d:subtitle>English</d:subtitle>   </d:language>   <d:actors>     <d:actor firstname="Liam" surname="Neeson">       <d:character>Qui Gon Jin</d:character>     </d:actor>     <d:actor firstname="Natalie" surname="Portman">       <d:character>Queen Amidala</d:character>     </d:actor>     <d:actor firstname="Ewan" surname="McGregor">       <d:character>Obi Wan Kenobi</d:character>     </d:actor>     <d:actor firstname="Jake" surname="Lloyd">       <d:character>Anakin Skywalker</d:character>     </d:actor>     <d:actor firstname="Ian" surname="McDiarmid">       <d:character>Senator Palpatine</d:character>     </d:actor>   </d:actors>   <d:directors>     <d:director firstname="George" surname="Lucas">       <d:favorite-film>                 The Empire Strikes Back             </d:favorite-film>     </d:director>   </d:directors>   <d:barcode>5039036007375</d:barcode>   <d:price currency="sterling">19.99</d:price> </d:dvd> 

SAX: Simple API for XML

The SAX model is based on the notion of a fast, forward-only and low memory footprint method of processing XML documents. To achieve these goals, the SAX parsers read through an XML document firing events whenever they encounter certain interesting parts of the document (in addition to having the ability to check documents against schemas). As it happens, those parts which the SAX parser seeks are wide-ranging and consist of everything from finding the beginning of a document (and its end) through to catching the occurrence of every open and close tag, and any textual data in between those tags.

To work with SAX, the application code must register for events that it is specifically interested in. For example, we might be particularly interested in extracting the details of a DVD from one of our dvd documents in order to store those details in some database. To achieve that, we would need to register the features of the document that we are interested in with the SAX parser. Then when the SAX parser parses the document, it will then inform us each time one of those features is encountered and our application code can use those signals to build up its own object model.

To use a SAX implementation within an application, as developers we must write code that subscribes to SAX events and pieces together a set of objects (or other structured data) from the events that the parser generates. The burden on the developer is to create a document handler capable of listening for the salient events being issued by the SAX parser and write a suitable object model to encapsulate data exposed by the SAX events.

If the object model developed to deal with the SAX events is lightweight, the SAX-oriented aspects of an application can be made lightning fast since SAX itself is also lightweight. The downside is, of course, that the document handler might be non-trivial to develop, especially for complex documents.

To illustrate, let's write some code to harvest the character information from a dvd document using the Java program shown in Figure 2-56. This is an undeniably long piece of code for essentially stripping out a few elements. Its length is due to the fact that the SAX parser only deals with creating events and not with the structured data associated with those events. In fact, the overwhelming size of this document is pared down to handling the various events that the SAX parser will issue as it parses a dvd document.

The startElement and endElement methods are called by the SAX parser when an element is entered or exited, respectively, and we use that event to determine whether we have found a character element. If we have found a character element, we simply set the _characterFound flag to true, whereas if we have not found a character element or if we are leaving an element altogether, then the flag is set to false.

The characters(…) method is called by the SAX parser whenever character data is encountered. If we are within a character element, i.e., the _characterFound flag is true, then we simply store the character data. All other character data is ignored. The main method simply sets up the parser and parses the document before pretty-printing the resulting characters to standard output.

On a positive note, the SAX approach offers good performance since we tailor the object model exactly to our needs (in this case it's just a linked list of characters). On a less positive note, SAX can be a complex tool to implement with due to the large number of possible events that we might have to write handlers for.

Figure 2-56. Creating a SAX-based application in Java.
 import java.io.*; import java.util.*; import org.xml.sax.*; import org.xml.sax.helpers.*; public class SAXExample extends DefaultHandler {     // Constants     private static final String _MY_DVD_NAMESPACE_URI =                                     "http://dvd.example.com";     private static final String _CHARACTER_ELEMENT_NAME =                                                  "character";     // Flag to remember if we are dealing with character     // data while parsing     private boolean   _characterFound = false;     // The data we're looking for in the document     private LinkedList _characters    = new LinkedList();     /**      * The method called when the start of a new element is      * found.      */     public void startElement(String namespaceURI,                              String localName,                              String qualifiedName,                              Attributes attributes)                             throws SAXException     {        // If the element is called "character" and is in the    // namespace "http://dvd.example.com" we've found one.        _characterFound =         namespaceURI.toLowerCase()         .equals(_MY_DVD_NAMESPACE_URI) &&         localName.toLowerCase()         .equals(_CHARACTER_ELEMENT_NAME);     }     /**      * The method called when the end of an element is found.      */     public void endElement(String namespaceURI,                            String localName,                            String qualifiedName)                           throws SAXException     {         _characterFound = false;     }    /**      * The method called when character data is found.      */     public void characters(char[] ch, int start, int length)                           throws SAXException     {         if(_characterFound)         {             _characters.add(new String(ch, start, length));         }     }     /**      * A convenience method to pretty-print the characters      * found.      */     public StringWriter outputCharacters()     {         StringWriter sw = new StringWriter();         for(int i = 0; i < _characters.size(); i++)         {             sw.write("<character xmlns=\"" +                       _MY_DVD_NAMESPACE_URI  + "\">");             sw.write((String)_characters.get(i));             sw.write("</character>\n");         }         return sw;     }     /**      * The starting point of the application.      */     public static void main(String[] args) throws Exception     {         // Check to see that we have a single URI argument         if(args.length != 1)         {             return;         }         SAXExample saxExample = new SAXExample();         XMLReader parser = null;         // Create parser         try         {              parser = XMLReaderFactory.createXMLReader(                       "org.apache.xerces.parsers.SAXParser");              // Tell the parser which object will handle             // SAX parsing events             parser.setContentHandler(saxExample);         }         catch (Exception e)         {             System.err.println("Unable to create Xerces SAX                                   parser - check classpath");         }         try         {             // The URL that sources the DVD goes here             //(i.e. perform a GET on some remote Web server).             parser.parse(args[0]);             // Dump the character information to screen.             System.out.println(                 saxExample.outputCharacters().toString());         }         catch (Exception e)         {             e.printStackTrace();         }     } } 

DOM: Document Object Model

DOM goes one step further than SAX and actually provides a simple tree-based object model on top of the basic XML processing and schema validation capabilities, usually built on top of an underlying SAX parser. When programming with a DOM parser, our application code interacts with an in-memory tree representation of the XML document. As such, DOM parsers are usually more heavyweight processors than their SAX equivalents since irrespective of the complexity or length of the XML document being processed, the same type of tree-based hierarchy is built.

Though this might not be the best data structure for any given application, the fact that DOM provides a simple object model "out of the box" is enticing and because of its simplicity, DOM has gained popularity. Indeed, we would generally only use SAX in preference to DOM where we have stringent performance requirements that rule out creating copies of documents in-memory, or where the tree-like mode of DOM is entirely unsuitable for the actual characteristics of the intended object model. Of course, it is possible to layer our own object model on top of that provided by DOM, thus providing both a natural fit for our application and leveraging DOM's ease-of-use. However, when using DOM as the basis for our own object models, we should be aware that we are consuming memory twice over once for our own objects and once for DOM.

Like SAX, the DOM API is well planned and straightforward to understand. To show some of features of DOM, we shall revisit the same DVD example that we previously tackled with SAX and illustrate the differences between the two approaches via the C# example shown in Figure 2-57.

Figure 2-57. Creating a DOM-Based application in C#.
 using System; using System.Xml; public class DOMExample {     private string getXMLDocument(string url)     {         // Grab the dvd document from its source         System.Net.WebClient wc = new System.Net.WebClient();         byte[] webData = wc.DownloadData(url);         // Get the downloaded data into a form suitable for         // XML processing         char[] charData = new char[webData.Length];         for(int i = 0; i < charData.Length; i++)         {             charData[i] = (char)webData[i];         }         string xmlStr = new String(charData);         // Clean up the document (first "<" and last ">" and         // everything in between)         int start = xmlStr.IndexOf("<", 0,                                         xmlStr.Length - 1);         int length = xmlStr.LastIndexOf(">") - start + 1;         // Return only the XML document parts         return xmlStr.Substring(start, length);     }     public static void Main(string[] args)     {     // Check to see that we have a single URI argument     if(args.Length != 1)     {         return;     }     string url = args[0];         DOMExample domExample = new DOMExample();         System.Xml.XmlDocument xmlDoc =                                 new System.Xml.XmlDocument();         xmlDoc.LoadXml(domExample.getXMLDocument(url));         // Search DOM tree for a set of elements with         // particular name and namespace         XmlNodeList xmlNodeList =                     xmlDoc.GetElementsByTagName("character",                                    "http://dvd.example.com");         for(int i = 0; i < xmlNodeList.Count; i++)         {             // Dump the contents of the elements we've found             // to standard output.             Console.WriteLine(xmlNodeList.Item(i).OuterXml);         }     } } 

The simple DOM-based application presented in Figure 2-57 is somewhat shorter than the previous SAX-based application. This simplicity does not stem from a different programming language or platform since (even platform zealots must agree) there is little difference between Java and .Net for simple XML processing. The gain in simplicity stems from the DOM processing model which automatically builds a data-structure to hold the contents of the XML document, and provides a simple API for searching and manipulating structure.

In fact the overwhelming majority of this application is spent checking that we have a clean XML document to deal with before we put it into our XML processing components. Since we chose to deal with the results of our remote call as an array of bytes returned via HTTP, we had to convert those bytes to characters and those characters to string, and then ensure that string did not contain any extraneous characters (such as the HTTP header information).

Once we are satisfied that we have our document in a clean form, we then submit it to the .Net DOM infrastructure. Internally, the infrastructure builds the DOM tree for us, and then to extract the character data it is simply a matter of searching for the element name (character) in the correct namespace (http://dvd.example.com). This search results in a list of possible answers, which we then dump to standard output.

While this is a suitable approach for a trivial example, this DOM-based method might not scale well in production environments. We are paying the price for the ease of use we have enjoyed in terms of memory and processing overhead. So while working with DOM is ultimately easier than SAX programmatically, it is always helpful to think about performance metrics and worth bearing in mind that SAX may be a better choice for some problems.

Extensible Stylesheet Transformation (XSLT) and XML Path Language (XPATH)

XSL is the acronym the W3C has assigned to the "Extensible Stylesheet Language." It consists of a language for transforming XML documents (XSLT) and an expression language used to access or reference parts of an XML document (XPath). It also refers to a formatting language called XML Formatting Objects (or XML-FO), but when most people talk about XSL what they are really talking about is XSLT and XPath. It is this subset of XSL technology that we investigate in this section.

The idea behind XSLT is to provide a declarative, rule-based XML scripting language that can be used to specify transformations on documents that is, to turn a document from one form into another based on some transformation rules. The benefit of this approach is that we can apply commodity XML processing tools to the processing of XML itself a recursive and inventive way of bootstrapping XML with XML. XPath supports XSLT by allowing parts of documents undergoing transformations to be referenced. Interestingly enough, XPath is not an XML-based syntax since its originators saw the value in being able to embed XPath expressions inside URIs and other non-XML identifiers. The canonical use of XPath is shown in Figure 2-58 where a trivial example of XSLT (with similarly simple XPath expressions) is presented.

XPath 1.0 has become perhaps the most important of the XSL technologies in the Web services arena and is now heavily used in other technologies like BPEL (see Chapter 6).


The stylesheet presented in Figure 2-58 is straightforward mainly because we haven't tried to do anything too ambitious and it is far shorter than either the SAX or even DOM versions of the code. The opening line of the document introduces some namespaces and defines what the result of the transformation will be without the prefix d. The subsequent six declarations tell the XSLT processor to do nothing with each of the elements that are named. For example, when the XSLT engine encounters a year element as a child of a dvd element, it triggers the execution of the matching template, which performs no processing. The end result of this "empty" template is that no output appears for the given element.

The template matching the element expressed in XPath as /d:dvd/d:actors/d:actor/d:character (i.e., the character element under the actor element, contained within the actors and dvd elements) does something slightly more ambitious. We create a new element in our output that has the same name as the current element we are examining (character), which is achieved by assigning the result of the XSLT name() function to the value held by the name attribute. We also give the newly created element a namespace (which, again, we borrow from the element currently under scrutiny) by referencing its namespace declaration (namespace-uri()) and assigning that value to the default namespace attribute for this element in our output.

Figure 2-58. A simple XSLT stylesheet.
 <xsl:stylesheet version="1.0"     xmlns:d="http://dvd.example.com"     xmlns:xsl="http://www.w3.org/1999/XSL/Transform"     exclude-result-prefixes="d">     <!-- We are not creating a document, so remove the            document declaration -->     <xsl:output method="xml" omit-xml-declaration="yes"/>     <!-- Do nothing with these elements -->     <xsl:template match="d:dvd/d:title"/>     <xsl:template match="d:dvd/d:year"/>     <xsl:template match="d:dvd/d:language"/>     <xsl:template match="d:dvd/d:directors"/>     <xsl:template match="d:dvd/d:barcode"/>     <xsl:template match="d:dvd/d:price"/>     <!-- Extract the value held by and character elements          encountered -->     <xsl:template match=         "d:dvd/d:actors/d:actor/d:character">         <xsl:element name="{name()}"             namespace="{namespace-uri()}">             <xsl:value-of select="."/>         </xsl:element>     </xsl:template> </xsl:stylesheet> 

The value-of element is then used in combination with the select="." attribute to select the value held within the current matching element where the axis "." is defined as "current context" in XPath. The net result of applying this template is to place the character information for each character encountered into the output from the XSLT engine and wrap that character data inside an appropriately namespaced XML element.

Although the example here has been necessarily trivial (since our goals were similarly trivial), XSLT is a powerful means of transforming XML documents. However, even this basic knowledge of what XSLT (and XPath) is and how it can be applied to XML documents will stand us in good stead as we finally venture out into the Web services world.



Developing Enterprise Web Services. An Architect's Guide
Developing Enterprise Web Services: An Architects Guide: An Architects Guide
ISBN: 0131401602
EAN: 2147483647
Year: 2003
Pages: 141

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net