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 thedata
directory off ofsamples
.There are seven sample directories distributed with JWSDP 1.6:
- cursor contains
CursorParse.java
, which illustrates how to use theXMLStreamReader
(cursor) API to read an XML file.- cursor2event contains
CursorApproachEventObject.java
, which illustrates how an application can get information as anXMLEvent
object when using cursor API.- data contains
BookCatalogue.xml
, which is the XML document used by the sample classes.- event contains
EventParse.java
, which illustrates how to use theXMLEventReader
(event iterator) API to read an XML file.- filter contains
MyStreamFilter.java
, which illustrates how to use the Stax Stream Filter APIs. In this example, the filter accepts onlyStartElement
andEndElement
events and filters out the remainder of the events.- readnwrite contains
EventProducerConsumer.java
, which illustrates how the StAX producer/consumer mechanism can be used to simultaneously read and write XML streams.- writer contains
CursorWriter.java
, which illustrates how to useXMLStreamWriter
to write an XML file programatically.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 SJSXPbuild.xml
file:
- all - Compile and run all classes; default target
- compile - Only compile classes; do not run
- cursor.CursorParse - Compile and run
./cursor/CursorParse.java
- cursor2event.CursorApproachEventObject - Compile and run
./cursor2event/CursorApproachEventObject.java
- event.EventParse - Compile and run
./event/Even
tParse.java- filter.MyStreamFilter - Compile and run
./filter/MyStreamFilter.java
- readnwrite.EventProducerConsumer - Compile and run .
/readnwrite/EventProducerConsumer.java
- writer.CursorWriter - Compile and run
./writer/CursorWriter.java
To run any of the Ant targets, change to the
<JWSDP_HOME>/sjsxp/samples
directory and invoke the target you want; for example:
Note: If the StAX (JSR 173) API JAR file is not named
jsr173_api.jar
, or is not in the same directory as thesjsxp.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 thestax.api.jar
property as shown:ant -Dstax.api.jar="<JSR 173 API LOCATION>" cursor.CursorParse
If Ant cannot find thesjsxp.jar
file, override thesjsxp.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 theCursorParse.java
sample:
- Change to the directory containing the CursorParse.java file:
cd <JWSDP_HOME>/sjsxp/samples/cursor
- Compile
CursorParse.java
:
javac -classpath ../lib/jsr173_api.jar CursorParse.java
Note that if the
jsr173_api.jar
is in yourCLASSPATH
, you do not need to use the-classpath
option here.- Run the
CursorParse
sample:
java -classpath .:../lib/sjsxp.jar:../lib/jsr173_api.jar cursor.CursorParse -x 1 ./samples/data/BookCatalogue.xml
Again, if the
jsr173_api.jar
andsjsxp.jar
files are in yourCLASSPATH
, 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 commonBookCatalogue
namespace. The contents ofBookCatalogue.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 & 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 toSTDOUT
.CursorApproachEventObject.java
Located in the
<JWSDP_HOME>/sjsxp/samples/cursor2event
directory,CursorApproachEventObject.java
demonstrates how to get information returned by anXMLEvent
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'sXMLEventReader
returns immutable and persistent event objects.XMLStreamReader
is more efficient, butXMLEventReader
is easier to use, as all the information related to a particular event is encapsulated in a returnedXMLEvent
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 anXMLEventAllocator
: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 toSTDOUT
. Note how theBook
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
:Creating an Event Reader
The next step is to create an instance of XMLEventReader:
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 toSTDOUT
. For example, an instance of theAuthor
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: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
:Creating a Stream Writer
The next step is to create an instance of
XMLStreamWriter
: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 namedCursorWriter-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 theEventParse.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 exceptStartElement
andEndElement
.Implementing the StreamFilter Class
The 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 toSTDOUT
. For example anAuthor
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 theXMLEventConsumer
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 toSTDOUT
:<?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 & Row</Publisher> <Cost currency="USD">2.95</Cost> </Book> </BookCatalogue>