Mapping XML to Java, Part 1

Employ the SAX API to map XML documents to Java objects

1 2 3 4 Page 4
Page 4 of 4

Although the second method is a little bulkier due to the replication of most of the class definition, this technique of swapping the ContentHandler is the first step toward a more generic solution to parsing with SAX. Swapping the ContentHandler is also another way for us to swap mugs under the running tap of a SAX parser.

The following code demonstrates the ContentHandler swap technique. The contents buffer is shared by the Example6 class and the other type-specific ContentHandler inner classes:

import org.xml.sax.*;
import org.xml.sax.helpers.*;
import java.io.*;
import java.util.*;
import common.*;
public class Example6 extends DefaultHandler {
   // XML Parser...
   XMLReader parser;
        // Mapping delegates...
        Example6Circle circleMapper = new Example6Circle();
        Example6Square squareMapper = new Example6Square();
        Example6Triangle triangleMapper = new Example6Triangle();
   // Local list of different shapes...
   private Vector circles = new Vector();
   private Vector triangles = new Vector();
   private Vector squares = new Vector();
   // Buffer for collecting data from
   // the "characters" SAX event.
   private CharArrayWriter contents = new CharArrayWriter();
   // Constructor with XML Parser...
   Example6( XMLReader parser ) {
           this.parser = parser;
   }
   // Override methods of the DefaultHandler class
   // to gain notification of SAX Events.
   //
        // See org.xml.sax.ContentHandler for all available events.
   //
   public void startElement( String namespaceURI,
               String localName,
              String qName,
              Attributes attr ) throws SAXException {
      contents.reset();
      if ( localName.equals( "Circle" ) ) {
                        Circle aCircle = new Circle();
         aCircle.name = attr.getValue( "name" );
         circles.addElement( aCircle );
         circleMapper.collectCircle( parser, this, aCircle );
      }
      if ( localName.equals( "Square" ) ) {
                    Square aSquare = new Square();
         aSquare.name = attr.getValue( "name" );
         squares.addElement( aSquare );
         squareMapper.collectSquare( parser, this, aSquare );
      }
      if ( localName.equals( "Triangle" ) ) {
         Triangle aTriangle = new Triangle();
         aTriangle.name = attr.getValue( "name" );
         triangles.addElement( aTriangle );
         triangleMapper.collectTriangle( parser, this, aTriangle);
      }
   }
   public void endElement( String namespaceURI,
               String localName,
              String qName ) throws SAXException {
      // Nothing left for the Example 6 mapper
      // to handle in the endElement SAX event.
   }
   public void characters( char[] ch, int start, int length )
                  throws SAXException {
      // accumulate the contents into a buffer.
      contents.write( ch, start, length );
   }
   public Vector getCircles() {
           return circles;
   }
   public Vector getSquares() {
           return squares;
   }
   public Vector getTriangles() {
           return triangles;
   }
   public static void main( String[] argv ){
      System.out.println( "Example6:" );
      try {
         // Create SAX 2 parser...
         XMLReader xr = XMLReaderFactory.createXMLReader();
         // Set the ContentHandler...
         Example6 ex6 = new Example6(xr);
         xr.setContentHandler( ex6 );
         // Parse the file...
         xr.parse( new InputSource(
               new FileReader( "Example6.xml" )) );
         // Display all circles to stdout...
         Circle c;
         Vector items = ex6.getCircles();
         Enumeration e = items.elements();
         while( e.hasMoreElements()){
                           c = (Circle) e.nextElement();
            c.print( System.out );
         }
         // Display all squares to stdout...
         Square s;
         items = ex6.getSquares();
         e = items.elements();
         while( e.hasMoreElements()){
                           s = (Square) e.nextElement();
            s.print( System.out );
         }
         // Display all triangle to stdout...
         Triangle t;
         items = ex6.getTriangles();
         e = items.elements();
         while( e.hasMoreElements()){
                           t = (Triangle) e.nextElement();
            t.print( System.out );
         }
      }catch ( Exception e )  {
         e.printStackTrace();
      }
   }
}
class Example6Circle extends DefaultHandler {
   // Local current circle reference...
        private Circle   currentCircle;
   // Parent...
   ContentHandler parent;
   // XML Parser
   XMLReader parser;
   // Buffer for collecting data from
   // the "characters" SAX event.
   private CharArrayWriter contents = new CharArrayWriter();
   public void collectCircle( XMLReader parser,
               ContentHandler parent,
               Circle newCircle ) {
      this.parent = parent;
      this.parser = parser;
      parser.setContentHandler( this );
      currentCircle = newCircle;
   }
   // Override methods of the DefaultHandler class
   // to gain notification of SAX Events.
   //
        // See org.xml.sax.ContentHandler for all available events.
   //
   public void startElement( String namespaceURI,
               String localName,
              String qName,
              Attributes attr ) throws SAXException {
      contents.reset();
   }
   public void endElement( String namespaceURI,
               String localName,
              String qName ) throws SAXException {
      if ( localName.equals( "x" ) ) {
                          currentCircle.x = 
                            Integer.valueOf
                            (contents.toString().trim()).intValue();
      }
      if ( localName.equals( "y" ) ) {
                          currentCircle.y = 
                              Integer.valueOf
                              (contents.toString().trim()).intValue();
      }
      if ( localName.equals( "width" ) ) {
                          currentCircle.width = 
                            Integer.valueOf
                            (contents.toString().trim()).intValue();
      }
      if ( localName.equals( "height" ) ) {
                          currentCircle.height = 
                             Integer.valueOf
                             (contents.toString().trim()).intValue();
      }
      if ( localName.equals( "Circle" ) ) {
                    // swap content handler back to parent
         parser.setContentHandler(parent);
      }
   }
   public void characters( char[] ch, int start, int length )
                  throws SAXException {
      // accumulate the contents into a buffer.
      contents.write( ch, start, length );
   }
}
class Example6Square extends DefaultHandler {
   // Local current square reference...
        private Square   currentSquare;
   // Parent...
   ContentHandler parent;
   // XML Parser
   XMLReader parser;
   // Buffer for collecting data from
   // the "characters" SAX event.
   private CharArrayWriter contents = new CharArrayWriter();
   public void collectSquare( XMLReader parser,
               ContentHandler parent,
               Square newSquare ) {
      this.parent = parent;
      this.parser = parser;
      parser.setContentHandler( this );
      currentSquare = newSquare;
   }
   // Override methods of the DefaultHandler class
   // to gain notification of SAX Events.
   //
        // See org.xml.sax.ContentHandler for all available events.
   //
   public void startElement( String namespaceURI,
               String localName,
              String qName,
              Attributes attr ) throws SAXException {
      contents.reset();
   }
   public void endElement( String namespaceURI,
               String localName,
              String qName ) throws SAXException {
      if ( localName.equals( "x" ) ) {
                          currentSquare.x = Integer.valueOf
                             (contents.toString().trim()).intValue();
      }
      if ( localName.equals( "y" ) ) {
                          currentSquare.y = Integer.valueOf
                             (contents.toString().trim()).intValue();
      }
      if ( localName.equals( "width" ) ) {
                          currentSquare.width = Integer.valueOf
                             (contents.toString().trim()).intValue();
      }
      if ( localName.equals( "height" ) ) {
                          currentSquare.height = Integer.valueOf
                             (contents.toString().trim()).intValue();
      }
      if ( localName.equals( "Square" ) ) {
                    // swap content handler back to parent
         parser.setContentHandler(parent);
      }
   }
   public void characters( char[] ch, int start, int length )
                  throws SAXException {
      // accumulate the contents into a buffer.
      contents.write( ch, start, length );
   }
}
class Example6Triangle extends DefaultHandler {
   // Local current triangle reference...
        private Triangle currentTriangle;
   // Parent...
   ContentHandler parent;
   // XML Parser
   XMLReader parser;
   // Buffer for collecting data from
   // the "characters" SAX event.
   private CharArrayWriter contents = new CharArrayWriter();
   public void collectTriangle( XMLReader parser,
               ContentHandler parent,
               Triangle newTriangle ) {
      this.parent = parent;
      this.parser = parser;
      parser.setContentHandler( this );
      currentTriangle = newTriangle;
   }
   public void endElement( String namespaceURI,
               String localName,
              String qName ) throws SAXException {
      if ( localName.equals( "x" ) ) {
                          currentTriangle.x = 
                             Integer.valueOf
                             (contents.toString().trim()).intValue();
      }
      if ( localName.equals( "y" ) ) {
                          currentTriangle.y = 
                            Integer.valueOf
                            (contents.toString().trim()).intValue();
      }
      if ( localName.equals( "width" ) ) {
                          currentTriangle.width = 
                             Integer.valueOf
                             (contents.toString().trim()).intValue();
      }
      if ( localName.equals( "height" ) ) {
                          currentTriangle.height = Integer.valueOf
                             (contents.toString().trim()).intValue();
      }
      if ( localName.equals( "Triangle" ) ) {
                    // swap content handler back to parent
         parser.setContentHandler(parent);
      }
   }
   public void characters( char[] ch, int start, int length )
                  throws SAXException {
      // accumulate the contents into a buffer.
      contents.write( ch, start, length );
   }
}

Notice that we get the same output from our shape classes:

Example6:
Circle: circ1 x: 10 y: 10 width: 3 height: 3
Square: sq1 x: 0 y: 0 width: 3 height: 3
Triange: tri1 x: 3 y: 0 width: 5 height: 3
Triange: tri2 x: 5 y: 0 width: 5 height: 3

Conclusion

We've demonstrated that SAX, when properly applied, has many advantages over the DOM API. We've covered some of the basic perspectives regarding SAX that allow us to effectively write XML to Java mapping code for simple and moderately complex XML documents. We've also highlighted some of the danger areas for applying the SAX API.

Finally, I hope you now understand the implications of using the DOM API in performance-sensitive environments, where the SAX API shines.

In the next article, we will tackle recursive XML structures, the ambiguous tag name problem, and the navigational aspects of SAX. These three threads come together in a general purpose class library that turns even the most complicated XML mapping code into a declarative style of coding that focuses on container management -- swapping beer glasses under the open tap.

Learn more about this topic

This story, "Mapping XML to Java, Part 1" was originally published by JavaWorld.

Copyright © 2000 IDG Communications, Inc.

1 2 3 4 Page 4
Page 4 of 4