XML.com: XML From the Inside Out
oreilly.comSafari Bookshelf.Conferences.

advertisement

A Survey of APIs and Techniques for Processing XML

July 09, 2003

Introduction

In recent times the landscape of APIs and techniques for processing XML has been reinvented as developers and designers learn from their experiences and some past mistakes. APIs such as DOM and SAX, which used to be the bread and butter of XML APIs, are giving way to new models of examining and processing XML. Although some of these techniques have become widespread among developers who primarily work with XML, they are still unknown to most developers. Nothing highlights this better than a recent article by Tim Bray, one of the coinventors of XML, entitled XML is too Hard for Programmers and the subsequent responses on Slashdot.

This article provides an overview of the current landscape of techniques for processing XML and runs the gamut from discussing old mainstays, such as push model APIs and tree model APIs as exemplified by SAX and DOM, to newer participants in the XML world such as cursor APIs and pull model parsers as exemplified by the .NET Framework's XPathNavigator and the XmlPull API respectively.

Sample Input Document

To give a clearer idea of what processing given the various API styles would look like I created the following sample document which describes a number of books I own and whether they have currently been loaned out to friends.


 <books>
  <book publisher="IDG books" on-loan="Sanjay">
    <title>XML Bible</title>
    <author>Elliotte Rusty Harold</author>
  </book>
  <book publisher="Addison-Wesley">
    <title>The Mythical Man Month</title>
    <author>Frederick Brooks</author>
  </book>
  <book publisher="WROX">
    <title>Professional XSLT 2nd Edition</title>
    <author>Michael Kay</author>
  </book>
  <book publisher="Prentice Hall" on-loan="Sander" >
   <title>Definitive XML Schema</title>
   <author>Priscilla Walmsley</author>
  </book>
  <book publisher="APress">
   <title>A Programmer's Introduction to C#</title>
   <author>Eric Gunnerson</author>
  </book>
</books>
   

For illustrative purposes each description of an XML processing technique will show a code sample that describes how to display the names of the persons who I've loaned books to and which books have been loaned to them. All of the code samples should produce the following output.

Sanjay was loaned XML Bible by Elliotte Rusty Harold
Sander was loaned Definitive XML Schema by Priscilla Walmsley

Push Model APIs

In a push model the XML producer (typically an XML parser) controls the pace of the application and informs the XML consumer when certain events occur. The classic example of this is the SAX API, where the XML consumer registers callbacks with the SAX parser, which invokes the callbacks as various parts of the XML document are seen.

The primary advantage of push model APIs when processing XML is that the entire XML document does not need to be stored in memory, only the information about the node currently being processed is needed. This makes it possible to process large XML documents which can range from several megabytes to a few gigabytes in size without incurring massive memory costs to the application. However it also means that certain context and state information such as the parents of the current node or its depth in the XML tree must be tracked by the programmer.

Another issue with push model parsers is that many developers find callbacks to be an unintuitive way to control program flow. Tim Bray described callbacks as being non-idiomatic and awkward when used from his programming language of choice.

The following code sample uses the SAX API in the Apache Xerces Parser to display the content of the title and author elements of books that have the on-loan attribute set.

    
import org.xml.sax.*; 
import org.xml.sax.helpers.*;
import java.io.*;


class LoanedBooksFinder extends DefaultHandler{

    public boolean borrowedBookSeen = false; 
    
    public void startElement(String uri, String name, 
                              String qName, Attributes atts){

     String borrower = atts.getValue("", "on-loan"); 

     if(qName.equals("book") && (borrower != null)){
	  borrowedBookSeen = true; 
	  System.out.print("\n" + borrower + " was loaned ");
     }if(qName.equals("author") && borrowedBookSeen){
	  System.out.println(" by ");
     }
	
    }


    public void endElement(String uri, String name, 
                            String qName){

	//reset flag
	if(qName.equals("book")){
	    borrowedBookSeen = false; 	
	}
    }

    public void characters(char[] ch, int start, int length){

	if(borrowedBookSeen){
	    for(int i = start; i < start + length; i++)
		System.out.print(ch[i]);
	}
    }

}

public class Test{

    public static void main(String[] args){
	
	try{ 
	
   string parser = "org.apache.xerces.parsers.SAXParser"

   XMLReader xr = XMLReaderFactory.createXMLReader(parsers);
   LoanedBooksFinder handler = new LoanedBooksFinder(); 
   xr.setContentHandler(handler); 
   xr.setErrorHandler(handler); 
	    
   FileReader r = new FileReader("books2.xml"); 
   xr.parse(new InputSource(r)); 

	}catch(SAXException se){
	    System.out.println("XML Parsing Error: " + se);
	}catch(IOException io){
	    System.out.println("File I/O Error: " + ioe);
	}
  
    }
}
    
   

It should be noted that to register callbacks one needs to create a class devoted to handling events from the SAX parser, either by implementing the ContentHandler interface or extending the DefaultHandler class.

Pull Model APIs

During pull model processing, the consumer of XML controls the program flow by requesting events from the XML producer as needed instead of waiting on events to be sent to it. This is very similar to the pseudocode described in Tim Bray's post as the typical text processing idiom. Like push model parsers, pull model XML parsers operate in a forward-only, streaming fashion while only showing information about a single node at any given time. This makes pull-based processing of XML as memory efficient as push-based processing but with a programming model that is more familiar to the average programmer.

Two notable pull model XML parsers are the .NET Framework's XmlReader class and the Common API for XML Pull Parsing. Programming using both APIs is fairly similar; one creates a loop that continually reads from the XML document until the end of the document is reached but acts solely open items of interest as they are seen.

The following code sample uses the .NET Framework's XmlTextReader class to display the contents of the title and author elements of books that have the on-loan attribute set.

using System; 
using System.IO; 
using System.Xml;

public class Test{


    static void Main(string[] args) {

      try{ 
      XmlTextReader reader = new XmlTextReader("books2.xml");
      ProcessBooks(reader);

      }catch(XmlException xe){
        Console.WriteLine("XML Parsing Error: " + xe);
      }catch(IOException ioe){
        Console.WriteLine("File I/O Error: " + ioe);
      }
    }  

    static void ProcessBooks(XmlTextReader reader) {
      
      while(reader.Read()){
      
        //keep reading until we see a book element 
        if(reader.Name.Equals("book") && 
	   (reader.NodeType == XmlNodeType.Element)){ 
          
	  if(reader.GetAttribute("on-loan") != null){ 
            ProcessBorrowedBook(reader);
          }else {
            reader.Skip();
          }
        }
      }
    }


   static void ProcessBorrowedBook(XmlTextReader reader){

 Console.Write("{0} was loaned ", 
                             reader.GetAttribute("on-loan"));
      
      
      while(reader.NodeType != XmlNodeType.EndElement && 
                                            reader.Read()){
       
       if (reader.NodeType == XmlNodeType.Element) {
          
	  switch (reader.Name) {
            case "title":              
              Console.Write(reader.ReadString());
              reader.Read(); // consume end tag
              break;
            case "author":
              Console.Write(" by ");
              Console.Write(reader.ReadString());
              reader.Read(); // consume end tag
              break;
          }
        }
      }
      Console.WriteLine();
    }
}       

Pull model parsers typically do not require a specialized class for handling XML processing since there is no requirement to implement specific interfaces or subclass certain classes for the purpose of registering callbacks. Also the need to explicitly track application states using boolean flags and similar variables is significantly reduced when using a pull model parser.

Pages: 1, 2

Next Pagearrow







close