Using StAX
In general, StAX programmers create XML stream readers, writers, and events by using the
XMLInputFactory
,XMLOutputFactory
andXMLEventFactory
classes. Configuration is done by setting properties on the factories, whereby implementation-specific settings can be passed to the underlying implementation using thesetProperty()
method on the factories. Similarly, implementation-specific settings can be queried using thegetProperty()
factory method.The
XMLInputFactory
,XMLOutputFactory
andXMLEventFactory
classes are described below, followed by discussions of resource allocation, namespace and attribute management, error handling, and then finally reading and writing streams using the cursor and iterator APIs.StAX Factory Classes
XMLInputFactory
The
XMLInputFactory
class lets you configure implementation instances of XML stream reader processors created by the factory. New instances of the abstract classXMLInputFactory
are created by calling thenewInstance()
method on the class. The static methodXMLInputFactory.newInstance()
is then used to create a new factory instance.Deriving from JAXP, the
XMLInputFactory.newInstance()
method determines the specificXMLInputFactory
implementation class to load by using the following lookup procedure:
- Use the
javax.xml.stream.XMLInputFactory
system property.- Use the
lib/xml.stream.properties
file in the JRE directory.- Use the Services API, if available, to determine the classname by looking in the
META-INF/services/javax.xml.stream.XMLInputFactory
files in jars available to the JRE.- Use the platform default
XMLInputFactory
instance.After getting a reference to an appropriate XMLInputFactory, an application can use the factory to configure and create stream instances. Table 17-4 lists the properties supported by
XMLInputFactory
. See the StAX specification for a more detailed listing.
XMLOutputFactory
New instances of the abstract class
XMLOutputFactory
are created by calling thenewInstance()
method on the class. The static methodXMLOutputFactory.newInstance()
is then used to create a new factory instance. The algorithm used to obtain the instance is the same as forXMLInputFactory
but references thejavax.xml.stream.XMLOutputFactory
system property.
XMLOutputFactory
supports only one property,javax.xml.stream.isRepairingNamespaces
. This property is required, and its purpose is to create default prefixes and associate them with Namespace URIs. See the StAX specification for a more information.XMLEventFactory
New instances of the abstract class
XMLEventFactory
are created by calling thenewInstance()
method on the class. The static methodXMLEventFactory.newInstance()
is then used to create a new factory instance. This factory references thejavax.xml.stream.XMLEventFactory
property to instantiate the factory. The algorithm used to obtain the instance is the same as forXMLInputFactory
andXMLOutputFactory
but references thejavax.xml.stream.XMLEventFactory
system property.There are no default properties for
XMLEventFactory
.Resources, Namespaces, and Errors
The StAX specification handles resource allocation, attributes and namespace, and errors and exceptions as described below.
Resource Resolution
The
XMLResolver
interface provides a means to set the method that resolves resources during XML processing. An application sets the interface onXMLInputFactory
, which then sets the interface on all processors created by that factory instance.Attributes and Namespaces
Attributes are reported by a StAX processor using lookup methods and strings in the cursor interface and
Attribute
andNamespace
events in the iterator interface. Note here that namespaces are treated as attributes, although namespaces are reported separately from attributes in both the cursor and iterator APIs. Note also that namespace processing is optional for StAX processors. See the StAX specification for complete information about namespace binding and optional namespace processing.Error Reporting and Exception Handling
All fatal errors are reported by way of
javax.xml.stream.XMLStreamException
. All nonfatal errors and warnings are reported using thejavax.xml.stream.XMLReporter
interface.Reading XML Streams
As described earlier in this chapter, the way you read XML streams with a StAX processor--and more importantly, what you get back--varies significantly depending on whether you are using the StAX cursor API or the event iterator API. The following two sections describe how to read XML streams with each of these APIs.
Using XMLStreamReader
The
XMLStreamReader
interface in the StAX cursor API lets you read XML streams or documents in a forward direction only, one item in the infoset at a time. The following methods are available for pulling data from the stream or skipping unwanted events:
- Get the value of an attribute
- Read XML content
- Determine whether an element has content or is empty
- Get indexed access to a collection of attributes
- Get indexed access to a collection of namespaces
- Get the name of the current event (if applicable)
- Get the content of the current event (if applicable)
Instances of
XMLStreamReader
have at any one time a single current event on which its methods operate. When you create an instance ofXMLStreamReader
on a stream, the initial current event is theSTART_DOCUMENT
state.TheXMLStreamReader.next()
method can then be used to step to the next event in the stream.Reading Properties, Attributes, and Namespaces
The
XMLStreamReader.next()
method loads the properties of the next event in the stream. You can then access those properties by calling theXMLStreamReader.getLocalName()
andXMLStreamReader.getText()
methods.When the
XMLStreamReader
cursor is over aStartElement
event, it reads the name and any attributes for the event, including the namespace. All attributes for an event can be accessed using an index value, and can also be looked up by namespace URI and local name. Note, however, that only the namespaces declared on the currentStartEvent
are available; previously declared namespaces are not maintained, and redeclared namespaces are not removed.XMLStreamReader Methods
XMLStreamReader
provides the following methods for retrieving information about namespaces and attributes:int getAttributeCount(); String getAttributeNamespace(int index); String getAttributeLocalName(int index); String getAttributePrefix(int index); String getAttributeType(int index); String getAttributeValue(int index); String getAttributeValue(String namespaceUri,String localName); boolean isAttributeSpecified(int index);Namespaces can also be accessed using three additional methods:
Instantiating an XMLStreamReader
This example, taken from the StAX specification, shows how to instantiate an input factory, create a reader, and iterate over the elements of an XML stream:
XMLInputFactory f = XMLInputFactory.newInstance(); XMLStreamReader r = f.createXMLStreamReader( ... ); while(r.hasNext()) { r.next(); }Using XMLEventReader
The
XMLEventReader
API in the StAX event iterator API provides the means to map events in an XML stream to allocated event objects that can be freely reused, and the API itself can be extended to handle custom events.XMLEventReader provides four methods for iteratively parsing XML streams:
For example, the following code snippet illustrates the
XMLEventReader
method declarations:package javax.xml.stream; import java.util.Iterator; public interface XMLEventReader extends Iterator { public Object next(); public XMLEvent nextEvent() throws XMLStreamException; public boolean hasNext(); public XMLEvent peek() throws XMLStreamException; ... }To read all events on a stream and then print them, you could use the following:
Reading Attributes
You can access attributes from their associated
javax.xml.stream.StartElement
, as follows:public interface StartElement extends XMLEvent { public Attribute getAttributeByName(QName name); public Iterator getAttributes(); }You can use the
getAttributes()
method on theStartElement
interface to use anIterator
over all the attributes declared on thatStartElement
.Reading Namespaces
Similar to reading attributes, namespaces are read using an
Iterator
created by calling thegetNamespaces()
method on theStartElement
interface. Only the namespace for the currentStartElement
is returned, and an application can get the current namespace context by usingStartElement.getNamespaceContext()
.Writing XML Streams
StAX is a bidirectional API, and both the cursor and event iterator APIs have their own set of interfaces for writing XML streams. As with the interfaces for reading streams, there are significant differences between the writer APIs for cursor and event iterator. The following sections describe how to write XML streams using each of these APIs.
Using XMLStreamWriter
The
XMLStreamWriter
interface in the StAX cursor API lets applications write back to an XML stream or create entirely new streams. XMLStreamWriter has methods that let you:Note that
XMLStreamWriter
implementations are not required to perform well-formedness or validity checks on input. While some implementations my perform strict error checking, others may not. The rules you choose to implement are set on properties provided by theXMLOutputFactory
class.The
writeCharacters(...)
method is used to escape characters such as&
,<
,>
, and"
. Binding prefixes can be handled by either passing the actual value for the prefix, by using thesetPrefix()
method, or by setting the property for defaulting namespace declarations.The following example, taken from the StAX specification, shows how to instantiate an output factory, create a writer and write XML output:
XMLOutputFactory output = XMLOutputFactory.newInstance(); XMLStreamWriter writer = output.createXMLStreamWriter( ... ); writer.writeStartDocument(); writer.setPrefix("c","http://c"); writer.setDefaultNamespace("http://c"); writer.writeStartElement("http://c","a"); writer.writeAttribute("b","blah"); writer.writeNamespace("c","http://c"); writer.writeDefaultNamespace("http://c"); writer.setPrefix("d","http://c"); writer.writeEmptyElement("http://c","d"); writer.writeAttribute("http://c","chris","fry"); writer.writeNamespace("d","http://c"); writer.writeCharacters("foo bar foo"); writer.writeEndElement(); writer.flush();This code generates the following XML (new lines are non-normative)
<?xml version='1.0' encoding='utf-8'?> <a b="blah" xmlns:c="http://c" xmlns="http://c"> <d:d d:chris="fry" xmlns:d="http://c"/>foo bar foo</a>Using XMLEventWriter
The
XMLEventWriter
interface in the StAX event iterator API lets applications write back to an XML stream or create entirely new streams. This API can be extended, but the main API is as follows:public interface XMLEventWriter { public void flush() throws XMLStreamException; public void close() throws XMLStreamException; public void add(XMLEvent e) throws XMLStreamException; // ... other methods not shown. }Instances of
XMLEventWriter
are created by an instance ofXMLOutputFactory
. Stream events are added iteratively, and an event cannot be modified after it has been added to an event writer instance.Attributes, Escaping Characters, Binding Prefixes
StAX implementations are required to buffer the last
StartElement
until an event other thanAttribute
orNamespace
is added or encountered in the stream. This means that when you add anAttribute
or aNamespace
to a stream, it is appended the currentStartElement
event.You can use the
Characters
method to escape characters like&
,<
,>
, and"
.The
setPrefix(...)
method can be used to explicitly bind a prefix for use during output, and thegetPrefix(...)
method can be used to get the current prefix. Note that by default,XMLEventWriter
adds namespace bindings to its internal namespace map. Prefixes go out of scope after the correspondingEndElement
for the event in which they are bound.