Sample Code

This section steps through the sample StAX code included in the JWSDP 1.6 bundle. All sample directories used in this section are located off the <JWSDP_HOME>/sjsxp/samples directory. The sample XML file used here is located in the data directory off of samples.

There are seven sample directories distributed with JWSDP 1.6:

Configuring Your Environment for Running the Samples

The instructions for configuring your environment are basically the same as those required for running the JWSDP in general. In addition to these general instructions, you should also set the following environment variables:

Running the Samples

The samples can be run either manually or by means of several Ant targets, defined in the <JWSDP_HOME>/sjsxp/samples/build.xml file. It is easiest to run the samples using the Ant targets.

When you run any of the samples, the compiled class files are placed in a directory named ./build. This directory is created if it does not exist already.

Running the Samples Using Ant

Use the Ant build file (build.xml) in the <JWSDP_HOME>/sjsxp/samples directory to run the SJSXP samples. There are eight targets defined in SJSXP build.xml file:

To run any of the Ant targets, change to the <JWSDP_HOME>/sjsxp/samples directory and invoke the target you want; for example:

cd jwsdp.home/sjsxp/samples
ant cursor.CursorParse 

Note: If the StAX (JSR 173) API JAR file is not named jsr173_api.jar, or is not in the same directory as the sjsxp.jar file, you will get an error when you run the samples. If this occurs, you should tell Ant the location of the StAX APIs by overriding the stax.api.jar property as shown:

ant -Dstax.api.jar="<JSR 173 API LOCATION>" cursor.CursorParse

If Ant cannot find the sjsxp.jar file, override the sjsxp.jar property as shown:

ant -Dsjsxp.jar="sjsxp.jar location" cursor.CursorParse


Running the Samples Manually

You can also run the samples manually. To do so, go to the <JWSDP_HOME/sjsxp/samples directory and change to the directory that contains the sample you want to run. For example, to run the CursorParse.java sample:

  1. Change to the directory containing the CursorParse.java file:
  2. cd <JWSDP_HOME>/sjsxp/samples/cursor

  3. Compile CursorParse.java:
  4. javac -classpath ../lib/jsr173_api.jar CursorParse.java

    Note that if the jsr173_api.jar is in your CLASSPATH, you do not need to use the -classpath option here.

  5. Run the CursorParse sample:
  6. java -classpath .:../lib/sjsxp.jar:../lib/jsr173_api.jar cursor.CursorParse -x 1 ./samples/data/BookCatalogue.xml

    Again, if the jsr173_api.jar and sjsxp.jar files are in your CLASSPATH, you do not need to use the -classpath option here.

Sample XML Document

The sample XML document, BookCatalogue.xml, used by most of the SJSXP sample classes is located in the <JWSDP_HOME>/sjsxp/samples/data directory, and is a simple book catalog based on the common BookCatalogue namespace. The contents of BookCatalogue.xml are listed below:

<?xml version="1.0"?>
<BookCatalogue xmlns="http://www.publishing.org">
  <Book>
    <Title>Yogasana Vijnana: the Science of Yoga</Title>
    <Author>Dhirendra Brahmachari</Author>
    <Date>1966</Date>
    <ISBN>81-40-34319-4</ISBN>
    <Publisher>Dhirendra Yoga Publications</Publisher>
    <Cost currency="INR">11.50</Cost>
  </Book>
  <Book>
    <Title>The First and Last Freedom</Title>
    <Author>J. Krishnamurti</Author>
    <Date>1954</Date>
    <ISBN>0-06-064831-7</ISBN>
    <Publisher>Harper &amp; Row</Publisher>
    <Cost currency="USD">2.95</Cost>
  </Book>
</BookCatalogue> 

CursorParse.java

Located in the <JWSDP_HOME>/sjsxp/samples/cursor directory, CursorParse.java demonstrates using the StAX cursor API to read an XML document.

Stepping Through Events

In this example, the client application pulls the next event in the XML stream by calling the next() method on the parser; for example:

try
  {
    for(int i =  0 ; i  < count ; i++)
      {
        //pass the file name.. all  relative  entity 
        //references will be resolved against  this as
        //base URI.
        XMLStreamReader xmlr  = 
xmlif.createXMLStreamReader(filename, new 
FileInputStream(filename));
        //when XMLStreamReader is created, it is positioned 
at START_DOCUMENT event.
        int eventType = xmlr.getEventType();
        //printEventType(eventType);
        printStartDocument(xmlr);
        //check if there are  more events  in  the input stream
        while(xmlr.hasNext())
          {
            eventType =  xmlr.next();
            //printEventType(eventType);
            //these functions  prints the information about 
the  particular event by calling relevant function
            printStartElement(xmlr);
            printEndElement(xmlr);
            printText(xmlr);
            printPIData(xmlr);
            printComment(xmlr);
          }
      } 

Note that next() just returns an integer constant corresponding to the event underlying the current cursor location. The application calls the relevant function to get more information related to the underlying event. There are various accessor methods which can be called when the cursor is at particular event.

Returning String Representations

Because the next() method only returns integers corresponding to underlying event types, you typically need to map these integers to string representations of the events; for example:

public final static  String getEventTypeString(int  eventType)
{
  switch  (eventType)
    {
        case XMLEvent.START_ELEMENT:
          return "START_ELEMENT";
        case XMLEvent.END_ELEMENT:
          return "END_ELEMENT";
        case XMLEvent.PROCESSING_INSTRUCTION:
          return "PROCESSING_INSTRUCTION";
        case XMLEvent.CHARACTERS:
          return "CHARACTERS";
        case XMLEvent.COMMENT:
          return "COMMENT";
        case XMLEvent.START_DOCUMENT:
          return "START_DOCUMENT";
        case XMLEvent.END_DOCUMENT:
          return "END_DOCUMENT";
        case XMLEvent.ENTITY_REFERENCE:
          return "ENTITY_REFERENCE";
        case XMLEvent.ATTRIBUTE:
          return "ATTRIBUTE";
        case XMLEvent.DTD:
          return "DTD";
        case XMLEvent.CDATA:
          return "CDATA";
        case XMLEvent.SPACE:
          return "SPACE";
    }
  return  "UNKNOWN_EVENT_TYPE ,   "+ eventType;
} 

Running the Sample

When you run the CursorParse sample, the class is compiled, and the XML stream is parsed and returned to STDOUT.

CursorApproachEventObject.java

Located in the <JWSDP_HOME>/sjsxp/samples/cursor2event directory, CursorApproachEventObject.java demonstrates how to get information returned by an XMLEvent object even when using the cursor API.

The idea here is that the cursor API's XMLStreamReader returns integer constants corresponding to particular events, where as the event iterator API's XMLEventReader returns immutable and persistent event objects. XMLStreamReader is more efficient, but XMLEventReader is easier to use, as all the information related to a particular event is encapsulated in a returned XMLEvent object. However, the disadvantage of event approach is the extra overhead of creating objects for every event, which consumes both time and memory.

With this mind, XMLEventAllocator can be used to get event information as an XMLEvent object, even when using the cursor API.

Instantiating an XMLEventAllocator

The first step is to create a new XMLInputFactory and instantiate an XMLEventAllocator:

XMLInputFactory xmlif = XMLInputFactory.newInstance();
System.out.println("FACTORY: " + xmlif);
xmlif.setEventAllocator(new XMLEventAllocatorImpl());
allocator = xmlif.getEventAllocator();
XMLStreamReader xmlr = xmlif.createXMLStreamReader(filename, 
new FileInputStream(filename)); 

Creating an Event Iterator

The next step is to create an event iterator:

int eventType = xmlr.getEventType();
while(xmlr.hasNext()){
  eventType = xmlr.next();
  //Get all "Book" elements as XMLEvent object
  if(eventType == XMLStreamConstants.START_ELEMENT && 
xmlr.getLocalName().equals("Book")){
    //get immutable XMLEvent
    StartElement event = getXMLEvent(xmlr).asStartElement();
    System.out.println("EVENT: " + event.toString());
  }
} 

Creating the Allocator Method

The final step is to create the XMLEventAllocator method:

private static XMLEvent getXMLEvent(XMLStreamReader reader) 
throws XMLStreamException{
  return allocator.allocate(reader);
} 

Running the Sample

When you run the CursorApproachEventObject sample, the class is compiled, and the XML stream is parsed and returned to STDOUT. Note how the Book events are returned as strings.

EventParse.java

Located in the <JWSDP_HOME>/sjsxp/samples/event directory, EventParse.java demonstrates how to use the StAX cursor API to read an XML document.

Creating an Input Factory

The first step is to create a new instance of XMLInputFactory:

XMLInputFactory factory = XMLInputFactory.newInstance();
System.out.println("FACTORY: " + factory); 

Creating an Event Reader

The next step is to create an instance of XMLEventReader:

XMLEventReader r = factory.createXMLEventReader(filename, new 
FileInputStream(filename)); 

Creating an Event Iterator

The third step is to create an event iterator:

XMLEventReader r = factory.createXMLEventReader(filename, new 
FileInputStream(filename));
while(r.hasNext()) {
  XMLEvent e = r.nextEvent();
  System.out.println(e.toString());
} 

Getting the Event Stream

The final step is to get the underlying event stream:

public final static String getEventTypeString(int eventType)
{
  switch (eventType)
    {
      case XMLEvent.START_ELEMENT:
        return "START_ELEMENT";
      case XMLEvent.END_ELEMENT:
        return "END_ELEMENT";
      case XMLEvent.PROCESSING_INSTRUCTION:
        return "PROCESSING_INSTRUCTION";
      case XMLEvent.CHARACTERS:
        return "CHARACTERS";
      case XMLEvent.COMMENT:
        return "COMMENT";
      case XMLEvent.START_DOCUMENT:
        return "START_DOCUMENT";
      case XMLEvent.END_DOCUMENT:
        return "END_DOCUMENT";
      case XMLEvent.ENTITY_REFERENCE:
        return "ENTITY_REFERENCE";
      case XMLEvent.ATTRIBUTE:
        return "ATTRIBUTE";
      case XMLEvent.DTD:
        return "DTD";
      case XMLEvent.CDATA:
        return "CDATA";
      case XMLEvent.SPACE:
        return "SPACE";
    }
  return "UNKNOWN_EVENT_TYPE " + "," + eventType;
} 

Running the Sample

When you run the EventParse sample, the class is compiled, and the XML stream is parsed as events and returned to STDOUT. For example, an instance of the Author element is returned as:

<['http://www.publishing.org']::Author>
Dhirendra Brahmachari
</['http://www.publishing.org']::Author> 

Note in this example that the event comprises an opening and closing tag, both of which include the namespace. The content of the element is returned as a string within the tags.

Similarly, an instance of the Cost element is returned as:

<['http://www.publishing.org']::Cost currency='INR'>
11.50
</['http://www.publishing.org']::Cost> 

In this case, the currency attribute and value are returned in the opening tag for the event.

See earlier in this chapter, in the "Iterator API" and "Reading XML Streams" sections, for a more detailed discussion of StAX event parsing.

CursorWriter.java

Located in the <JWSDP_HOME>/sjsxp/samples/writer directory, CursorWriter.java demonstrates how to use the StAX cursor API to write an XML stream.

Creating the Output Factory

The first step is to create an instance of XMLOutputFactory:

XMLOutputFactory xof =  XMLOutputFactory.newInstance(); 

Creating a Stream Writer

The next step is to create an instance of XMLStreamWriter:

XMLStreamWriter xtw = null; 

Writing the Stream

The final step is to write the XML stream. Note that the stream is flushed and closed after the final EndDocument is written:

xtw = xof.createXMLStreamWriter(new FileWriter(fileName));
xtw.writeComment("all elements here are explicitly in the HTML 
namespace");
xtw.writeStartDocument("utf-8","1.0");
xtw.setPrefix("html", "http://www.w3.org/TR/REC-html40");
xtw.writeStartElement("http://www.w3.org/TR/REC-
html40","html");
xtw.writeNamespace("html", "http://www.w3.org/TR/REC-html40");
xtw.writeStartElement("http://www.w3.org/TR/REC-
html40","head");
xtw.writeStartElement("http://www.w3.org/TR/REC-
html40","title");
xtw.writeCharacters("Frobnostication");
xtw.writeEndElement();
xtw.writeEndElement();
xtw.writeStartElement("http://www.w3.org/TR/REC-
html40","body");
xtw.writeStartElement("http://www.w3.org/TR/REC-html40","p");
xtw.writeCharacters("Moved to");
xtw.writeStartElement("http://www.w3.org/TR/REC-html40","a");
xtw.writeAttribute("href","http://frob.com");
xtw.writeCharacters("here");
xtw.writeEndElement();
xtw.writeEndElement();
xtw.writeEndElement();
xtw.writeEndElement();
xtw.writeEndDocument();
xtw.flush();
xtw.close(); 

Running the Sample

When you run the CursorWriter sample, the class is compiled, and the XML stream is parsed as events and written to a file named CursorWriter-Output:

<!--all elements here are explicitly in the HTML namespace-->
<?xml version="1.0" encoding="utf-8"?>
<html:html xmlns:html="http://www.w3.org/TR/REC-html40">
<html:head>
<html:title>Frobnostication</html:title></html:head>
<html:body>
<html:p>Moved to
<html:a href="http://frob.com">here</html:a>
</html:p>
</html:body>
</html:html> 

Note that in the actual CursorWriter-Output file, this stream is written without any linebreaks; the breaks have been added here to make the listing easier to read. In this example, as with the object stream in the EventParse.java sample, the namespace prefix is added to both the opening and closing HTML tags. This is not required by the StAX specification, but it is good practice when the final scope of the output stream is not definitively known.

MyStreamFilter.java

Located in the <JWSDP_HOME>/sjsxp/samples/filter directory, MyStreamFilter.java demonstrates how to use the StAX stream filter API to filter out events not needed by your application. In this example, the parser filters out all events except StartElement and EndElement.

Implementing the StreamFilter Class

The MyStreamFilter implements javax.xml.stream.StreamFilter:

public class MyStreamFilter implements 
javax.xml.stream.StreamFilter{ 

Creating an Input Factory

The next step is to create an instance of XMLInputFactory. In this case, various properties are also set on the factory:

XMLInputFactory xmlif = null ;
try{
xmlif = XMLInputFactory.newInstance();
xmlif.setProperty(XMLInputFactory.IS_REPLACING_ENTITY_REFERENC
ES,Boolean.TRUE);
xmlif.setProperty(XMLInputFactory.IS_SUPPORTING_EXTERNAL_ENTIT
IES,Boolean.FALSE);
xmlif.setProperty(XMLInputFactory.IS_NAMESPACE_AWARE , 
Boolean.TRUE);
xmlif.setProperty(XMLInputFactory.IS_COALESCING , 
Boolean.TRUE);
}catch(Exception ex){
  ex.printStackTrace();
}
System.out.println("FACTORY: " + xmlif);
System.out.println("filename = "+ filename); 

Creating the Filter

The next step is to instantiate a file input stream and create the stream filter:

FileInputStream fis = new FileInputStream(filename);
        
XMLStreamReader xmlr = 
xmlif.createFilteredReader(xmlif.createXMLStreamReader(fis), 
new MyStreamFilter());

int eventType = xmlr.getEventType();
printEventType(eventType);
while(xmlr.hasNext()){
  eventType = xmlr.next();
  printEventType(eventType);
  printName(xmlr,eventType);
  printText(xmlr);
  if(xmlr.isStartElement()){
    printAttributes(xmlr);
  }
  printPIData(xmlr);
  System.out.println("-----------------------------");
} 

Capturing the Event Stream

The next step is to capture the event stream. This is done in basically the same way as in the EventParse.java sample.

Filtering the Stream

The final step is the filter the stream:

public boolean accept(XMLStreamReader reader) {
  if(!reader.isStartElement() && !reader.isEndElement())
    return false;
  else
    return true;
} 

Running the Sample

When you run the MyStreamFilter sample, the class is compiled, and the XML stream is parsed as events and returned to STDOUT. For example an Author event is returned as follows:

EVENT TYPE(1):START_ELEMENT
HAS NAME: Author
HAS NO TEXT
HAS NO ATTRIBUTES
-----------------------------
EVENT TYPE(2):END_ELEMENT
HAS NAME: Author
HAS NO TEXT
----------------------------- 

Similarly, a Cost event is returned as follows:

EVENT TYPE(1):START_ELEMENT
HAS NAME: Cost
HAS NO TEXT

HAS ATTRIBUTES: 
ATTRIBUTE-PREFIX: 
ATTRIBUTE-NAMESP: null
ATTRIBUTE-NAME:   currency
ATTRIBUTE-VALUE:  INR
ATTRIBUTE-TYPE:  CDATA

-----------------------------
EVENT TYPE(2):END_ELEMENT
HAS NAME: Cost
HAS NO TEXT
----------------------------- 

See earlier in this chapter, in the "Iterator API" and "Reading XML Streams" sections, for a more detailed discussion of StAX event parsing.

EventProducerConsumer.java

Located in the <JWSDP_HOME>/sjsxp/samples/readnwrite directory, EventProducerConsumer.java demonstrates how to use a StAX parser simultaneously as both a producer and a consumer.

The StAX XMLEventWriter API extends from the XMLEventConsumer interface, and is referred to as an event consumer. By contrast, XMLEventReader is an event producer. StAX supports simultaneous reading and writing, such that it is possible to read from one XML stream sequentially and simultaneously write to another stream.

This sample shows how the StAX producer/consumer mechanism can be used to read and write simultaneously. This sample also shows how a stream can be modified, and new events can be added dynamically and then written to different stream.

Creating an Event Producer/Consumer

The first step is to instantiate an event factory and then create an instance of an event producer/consumer:

XMLEventFactory m_eventFactory=XMLEventFactory.newInstance();
public EventProducerConsumer() {
}
.
.
.
try{
  EventProducerConsumer ms = new EventProducerConsumer();
  
  XMLEventReader reader = 
XMLInputFactory.newInstance().createXMLEventReader(new 
java.io.FileInputStream(args[0]));
  XMLEventWriter writer = 
XMLOutputFactory.newInstance().createXMLEventWriter(System.out
); 

Creating an Iterator

The next step is to create an iterator to parse the stream:

while(reader.hasNext())
  {
    XMLEvent event = (XMLEvent)reader.next();
    if(event.getEventType() == event.CHARACTERS)
      {
        
writer.add(ms.getNewCharactersEvent(event.asCharacters()));
      }
    else
      {
        writer.add(event);
      }
  }
writer.flush(); 

Creating a Writer

The final step is to create a stream writer in the form of a new Character event:

Characters getNewCharactersEvent(Characters event){
  if(event.getData().equalsIgnoreCase("Name1")){
    return 
m_eventFactory.createCharacters(Calendar.getInstance().getTime
().toString());

  }
  //else return the same event
  else return event;
} 

Running the Sample

When you run the EventProducerConsumer sample, the class is compiled, and the XML stream is parsed as events and written back to STDOUT:

<?xml version="1.0" encoding="UTF-8"?>
<BookCatalogue xmlns="http://www.publishing.org">
  <Book>
    <Title>Yogasana Vijnana: the Science of Yoga</Title>
    <Author>Dhirendra Brahmachari</Author>
    <Date>1966</Date>
    <ISBN>81-40-34319-4</ISBN>
    <Publisher>Dhirendra Yoga Publications</Publisher>
    <Cost currency="INR">11.50</Cost>
  </Book>

  <Book>
    <Title>The First and Last Freedom</Title>
    <Author>J. Krishnamurti</Author>
    <Date>1954</Date>
    <ISBN>0-06-064831-7</ISBN>
    <Publisher>Harper &amp; Row</Publisher>
    <Cost currency="USD">2.95</Cost>
  </Book>
</BookCatalogue>