blob: 61b884e0f4497340a00fa9c00a69f7d2d9d94eda [file] [log] [blame]
<?xml version="1.0" encoding="ISO-8859-1"?>
<document>
<properties>
<title>OM Tutorial</title>
</properties>
<head>
<meta http-equiv="content-type" content="text/html; charset=iso-8859-1"/>
<meta content="text/html; charset=iso-8859-1"/>
</head>
<body lang="en">
<!-- Header section
<table width="100%">
<tr>
<td align="left"><h1>Apache Axis2</h1></td>
<td align="right"><img alt="" src="images/axis2.jpg"/></td>
</tr>
</table>
end of header section -->
<a name="intro"></a>
<h2>Introduction</h2>
<a name="what"></a>
<h3>What is OM?</h3>
<p>OM stands for Object Model (also known as AXIOM - AXis Object Model) and
refers to the XML infoset model that is initially developed for Apache Axis2.
XML infoset refers to the information included inside the XML, and for
programmatic manipulation it is convenient to have a representation of this
XML infoset in a language specific manner. For an object oriented language
the obvious choice is a model made up of objects. <a
href="http://www.w3.org/DOM/">DOM</a> and <a
href="http://www.jdom.org/">JDOM</a> are two such XML models. OM is
conceptually similar to such an XML model by its external behavior but deep
down it is very much different. The objective of this tutorial is to
introduce the basics of OM and explain the best practices to be followed
while using OM. However, before diving in to the deep end of OM it is better
to skim the surface and see what it is all about!</p>
<a name="whom"></a>
<h3>For whom is this Tutorial?</h3>
<p>This tutorial can be used by anyone who is interested in OM and needs to
gain a deeper knowledge about the model. However, it is assumed that the
reader has a basic understanding of the concepts of XML (such as <a
href="http://www.w3.org/TR/REC-xml-names/">Namespaces</a>) and a working
knowledge of tools such as <a href="http://ant.apache.org/">Ant</a>.
Knowledge in similar object models such as DOM will be quite helpful in
understanding OM, mainly to highlight the differences and similarities
between the two, but such knowledge is not assumed. Several links are listed
in the <a href="#links">Appendix</a> that will help understand the basics of
XML.</p>
<a name="pullparsing"></a>
<h3>What is Pull Parsing?</h3>
Pull parsing is a recent trend in XML processing. The previously popular XML
processing frameworks such as <a
href="http://en.wikipedia.org/wiki/Simple_API_for_XML">SAX</a> and <a
href="http://en.wikipedia.org/wiki/Document_Object_Model">DOM</a> were
"push-based" which means the control of the parsing was in the hands of the
parser itself. This approach is fine and easy to use, but it was not
efficient in handling large XML documents since a complete memory model will
be generated in the memory. Pull parsing inverts the control and hence the
parser only proceeds at the users command. The user can decide to store or
discard events generated from the parser. OM is based on pull parsing. To
learn more about XML pull parsing see the <a
href="http://www.bearcave.com/software/java/xml/xmlpull.html">XML pull
parsing introduction</a>.
<a name="history"></a>
<h3>A Bit of History</h3>
<p>The original OM was proposed as a store for the pull parser events for
later processing, at the Axis summit held in Colombo, Sri Lanka, in September
2004. However, this approach was soon improved and OM was pursued as a
complete <a href="http://dret.net/glossary/xmlinfoset">XML infoset</a> model
due to its flexibility. Several implementation techniques were attempted
during the initial phases. The two most promising techniques were the table
based technique and the link list based technique. During the intermediate
performance tests the link list based technique proved to be much more memory
efficient for smaller and mid sized XML documents. The advantage of the table
based OM was only visible for the large and very large XML documents, and
hence, the link list based technique was chosen as the most suitable. Initial
efforts were focused on implementing the XML infoset (XML Information Set)
items which are relevant to the SOAP specification (DTD support, Processing
Instruction support, etc were not considered). The advantage of having a
tight integration was evident at this stage and this resulted in having SOAP
specific interfaces as part of OM rather than a layer on top of it. OM was
deliberately made <a
href="http://en.wikipedia.org/wiki/Application_programming_interface">API</a>
centric. It allows the implementations to take place independently and
swapped without affecting the program later.</p>
<a name="features"></a>
<h3>Features of OM</h3>
<p>OM is a lightweight, deferred built XML infoset representation based on
StAX (<a href="http://www.jcp.org/en/jsr/detail?id=173">JSR 173</a>), which
is the standard streaming pull parser API. The object model can be
manipulated as flexibly as any other object model (Such as <a
href="http://www.jdom.org/">JDOM</a>), but underneath, the objects will be
created only when they are absolutely required. This leads to much less
memory intensive programming. Following is a short feature overview of OM.</p>
<ul>
<li><strong>Lightweight</strong>: OM is specifically targeted to be
lightweight. This is achieved by reducing the depth of the hierarchy,
number of methods and the attributes enclosed in the objects. This makes
the objects less memory intensive.</li>
<li><strong>Deferred building</strong>: By far this is the most important
feature of OM. The objects are not made unless a need arises for them.
This passes the control of building over to the object model itself
rather than an external builder.</li>
<li><strong>Pull based</strong>: For a deferred building mechanism a pull
based parser is required. OM is based on <a
href="http://today.java.net/pub/a/today/2006/07/20/introduction-to-stax.html">StAX</a>,
the standard pull parser API.</li>
</ul>
<!-- Special section -->
<p class="special"></p>
<table width="100%">
<tbody>
<tr>
<td><img src="images/OM005.gif" alt="Remember this" width="35" height="57"/></td>
<td class="special-td">OM is tightly bound to StAX API. To work with OM
a StAX compliant parser and the API <i>must</i> be present in the
classpath.</td>
<td></td>
</tr>
</tbody>
</table>
<!-- End of special section -->
<p>The Following image shows how OM API is viewed by the user</p>
<!-- Image -->
<br/>
<p align="center" class="img"><img alt="" src="images/archi006.jpg"
class="img" width="490" height="282"/></p>
<br/>
<p align="center" class="img-title">Figure 1</p><br/>
<!-- End of Image -->
<p>OM Builder wraps the raw xml character stream through the StAX reader API.
Hence, the complexities of the pull event stream is transparent to the
user.</p>
<a name="caching"></a>
<h3>A Bit About Caching</h3>
<p>Since OM is a deferred built object model, It incorporates the concept of
caching. Caching refers to the creation of the objects while parsing the pull
stream. The reason why this is so important is because caching can be turned
off in certain situations. If so the parser proceeds without building the
object structure. User can extract the raw pull stream from OM and use that
instead of the OM. In this case it is sometimes beneficial to switch off
caching. The <a href="#advanced">Advanced Operations</a> section explains
more on accessing the raw pull stream and switching on and off the
caching.</p>
<a name="soap"></a>
<h3>Where Does SOAP Come into Play?</h3>
<p>In a nutshell <a
href="http://www.w3schools.com/SOAP/soap_intro.asp">SOAP</a> is an
information exchange protocol based on XML. SOAP has a defined set of XML
elements that should be used in messages. Since Axis2 is a "SOAP Engine" and
OM is built for Axis2, a set of SOAP specific objects were also defined along
with OM. These SOAP Objects are extensions of the general OM objects. <!--<p>This abstraction provides</p>
<ul>
<li>deferred building support</li>
<li>Caching</li>
</ul>
<p>AXIOM has the flexibility to plug in any builder which implements the given builders interface. The upcoming sections of this tutorial deal with the creation and usage of OM for manipulating SOAP (and to some extent nonSOAP) based XML documents.</p>
-->
</p>
<a name="working"></a>
<h2>Working with OM</h2>
<a name="binary"></a>
<h3>Obtaining the OM Binary</h3>
<p>There are two methods through which the OM-binary can be obtained:</p>
<ol>
<li>The easiest way to obtain the OM binary is to <a
href="http://ws.apache.org/commons/axiom/download.cgi">download</a> the
latest release. After the source download, OM-binary can be built. For
both MS Windows and Linux, move it to the project directory and execute
the command "maven jar". All other necessary jars will be automatically
downloaded. When the build is completed successfully, the
axiom-api-&lt;version&gt;.jar and axiom-impl-&lt;version&gt;.jar can be
found in the newly created "targets" directory.</li>
<li>However, more adventurous users can build the OM from source, which is
described in the next section. Detailed information on getting source
from SVN repository is found <a href="svn.html"
target="_blank">here</a>.</li>
</ol>
<p>Once the OM-binary is obtained by any of the above ways , it should be
included in the classpath for any of the OM based programs to work.
Subsequent sections of this tutorial assume that this build step is complete
and <font color="red"><span
style="color: #000000">axiom-api-&lt;version&gt;.jar </span> <span
style="color: #000000">and</span> <span
style="color: #000000">axiom-impl-&lt;version&gt;.jar</span></font> are
present in the classpath along with the StAX API jar file and a StAX
implementation.</p>
<a name="creation"></a>
<h3>Creation</h3>
<p>Creation is the first and foremost action when using an Object
representation. This part explains how OM can be built from an existing
document or simply programmatically. OM provides a notion of a factory and a
builder to create objects. The factory helps to keep the code at the
interface level and the implementations separately as shown in <a
href="#fig2">Figure 2</a>. Since OM is tightly bound to StAX, a StAX
compliant reader should be created first with the desired input stream. Then
one can select one of the different builders available.</p>
<p>StAXOMBuilder will build pure XML infoset compliant object model whilst
the SOAPModelBuilder returns SOAP specific objects (such as the SOAPEnvelope,
which are sub classes of the OMElement) through its builder methods. The
following piece of code shows the correct method of creating an OM document
from an input stream.</p>
<div align="left">
<a name="list1"></a>
<p><b>Code Listing 1</b></p>
</div>
<source><pre>//create the parser<br/>
XMLStreamReader parser = XMLInputFactory.newInstance().createXMLStreamReader(new FileInputStream(file));
<br/>//create the builder<br/>
StAXOMBuilder builder = new StAXOMBuilder(parser);
//get the root element (in this case the envelope)<br/>
OMElement documentElement = builder.getDocumentElement();</pre>
</source>
<p>As the example shows, creating an OM from an input stream is pretty
straightforward. However, elements and nodes can be created programmatically
to modify the structure as well. The recommended way to create OM objects
programmatically is to use the factory. OMAbstractFactory.getOMFactory() will
return the proper factory and the creator methods for each type that should
be called. Currently OM has two builders, namely the OM builder
(StAXOMBuilder) and the SOAP model builder (StAXSOAPModelBuilder). These
builders provide the necessary information to the XML infoset model to build
itself.</p>
<!-- Image -->
<a name="fig2"></a>
<br/>
<p align="center" class="img"><img src="images/archi007.jpg"
alt="OM Structure 2" class="img" width="420" height="246"/></p>
<br/>
<p align="center" class="img-title">Figure 2</p>
<!-- End of Image -->
<br/>
<p>A simple example is shown below:</p>
<div align="left">
<p><b>Code Listing 2</b></p>
</div>
<source><pre class="code">//create a factory
OMFactory factory = OMAbstractFactory.getOMFactory();
//use the factory to create two namespace objects
OMNamespace ns1 = factory.createOMNamespace("bar","x");
OMNamespace ns2 = factory.createOMNamespace("bar1","y");
//use the factory to create three elements
OMElement root = factory.createOMElement("root",ns1);
OMElement elt11 = factory.createOMElement("foo1",ns1);
OMElement elt12 = factory.createOMElement("foo2",ns1);</pre></source>
<p>The reason as to have a set of factory.createXXX methods is to cater for
different implementations, but keep the programmers code intact. Its highly
recommended to use the factory for creating OM objects as this will ease the
switching of different OM implementations. Several differences exist between
a programmatically created OMNode and a conventionally built OMNode. The most
important difference is that the former will have no builder object enclosed,
where as the latter always carries a reference to its builder.</p>
<p>As stated earlier in this tutorial, since the object model is built as and
when required, each and every OMNode should have a reference to its builder.
If this information is not available, it is due to the object being created
without a builder. This difference becomes evident when the user tries to get
a non caching pull parser from the OMElement. This will be discussed in more
detail in the <a href="#advanced">Advanced Operations</a> section.</p>
<p>In order to understand the requirement of the builder reference in each
and every OMNode, consider the following scenario. Assume that the parent
element is built but the children elements are not. If the parent is asked to
iterate through its children, this information is not readily available to
the parent element and it should build its children first before attempting
to iterate them. In order to provide a reference of the builder, each and
every node of an OM structure should carry the reference to its builder. Each
and every OMNode carries a flag that states its build status. Apart from this
restriction there are no other constraints that keep the programmer away from
mixing up programmatically made OMNode objects with OMNode objects built from
builders.</p>
<p>The SOAP object hierarchy is made in the most natural way for a
programmer. An inspection of the API will show that it is quite close to the
SAAJ API but with no bindings to DOM or any other model. The SOAP classes
extend basic OM classes (such as the OMElement) hence, one can access a SOAP
document either with the abstraction of SOAP or drill down to the underlying
XML Object model with a simple casting.</p>
<!-- The following illustration of the actual class diagram will be helpful in understanding this.
Need an image here -->
<a name="nodes"></a>
<h3>Addition of Nodes</h3>
<p>Addition and removal methods are primarily defined in the OMElement
interface. The following are the most important in adding nodes.</p>
<div align="left">
<p><b>Code Listing 3</b></p>
</div>
<source><pre class="code">public void addChild(OMNode omNode);
public void addAttribute(OMAttribute attr);</pre></source>
<p>This code segment shows how the addition takes place. Note that it is
related to the code listings 1 &amp; 2 in the <a
href="#creation">Creation</a> section.</p>
<div align="left">
<p><b>Code Listing 4</b></p>
</div>
<source><pre class="code">//set the children
elt11.addChild(elt21);
elt12.addChild(elt22);
root.addChild(elt11);
root.addChild(elt12);</pre></source>
<ul>
<li>AddChild method will always add the child as the last child of the
parent.</li>
<li><p>A given node can be removed from the tree by calling the detach()
method. A node can also be removed from the tree by calling the remove
method of the returned iterator which will also call the detach method of
the particular node internally.</p>
</li>
<li>Namespaces are a tricky part of any XML object model and is the same in
OM. However, the interface to the namespace have been made very simple.
OMNamespace is the class that represents a namespace with intentionally
removed setter methods. This makes the OMNamespace immutable and allows
the underlying implementation to share the objects without any
difficulty.</li>
</ul>
<p>Following are the important methods available in OMElement to handle
namespaces.</p>
<div align="left">
<p><b>Code Listing 5</b></p>
</div>
<source><pre class="code">public OMNamespace declareNamespace(String uri, String prefix);
public OMNamespace declareNamespace(OMNamespace namespace);
public OMNamespace findNamespace(String uri, String prefix) throws OMException;</pre></source>
<p>The declareNamespaceXX methods are fairly straightforward. Add a namespace
to namespace declarations section. Note that a namespace declaration that has
already being added will not be added twice. findNamespace is a very handy
method to locate a namespace object higher up the object tree. It searches
for a matching namespace in its own declarations section and jumps to the
parent if it's not found. The search progresses up the tree until a matching
namespace is found or the root has been reached.</p>
<p>During the serialization a directly created namespace from the factory
will only be added to the declarations when that prefix is encountered by the
serializer. More of the serialization matters will be discussed in the <a
href="#serializer">Serializer</a> section.</p>
<p>The following simple code segment shows how the namespaces are dealt in
OM</p>
<div align="left">
<a name="list6"></a>
<p><b>Code Listing 6</b></p>
</div>
<source><pre class="code">OMFactory factory = OMAbstractFactory.getOMFactory();
OMNamespace ns1 = factory.createOMNamespace("bar","x");
OMElement root = factory.createOMElement("root",ns1);
OMNamespace ns2 = root.declareNamespace("bar1","y");
OMElement elt1 = factory.createOMElement("foo",ns1);
OMElement elt2 = factory.createOMElement("yuck",ns2);
OMText txt1 = factory.createOMText(elt2,"blah");
elt2.addChild(txt1);
elt1.addChild(elt2);
root.addChild(elt1);</pre></source>
<p>Serialization of the root element produces the following XML:</p>
<pre class="xml">&lt;x:root xmlns:x="bar" xmlns:y="bar1"&gt;&lt;x:foo&gt;&lt;y:yuck&gt;blah&lt;/y:yuck&gt;&lt;/x:foo&gt;&lt;/x:root&gt;</pre>
<a name="traversing"></a>
<h3>Traversing</h3>
<p>Traversing the object structure can be done in the usual way by using the
list of children. Note however, that the child nodes are returned as an
iterator. The Iterator supports the 'OM way' of accessing elements and is
more convenient than a list for sequential access. The following code sample
shows how the children can be accessed. The children are of the type OMNode
that can either be OMText or OMElement.</p>
<div align="left">
<p><b>Code Listing 7</b></p>
</div>
<source><pre class="code">Iterator children = root.getChildren();
while(children.hasNext()){
OMNode node = (OMNode)children.next();
}</pre></source>
<p>Apart from this, every OMNode has links to its siblings. If more thorough
navigation is needed the nextSibling() and PreviousSibling() methods can be
used. A more selective set can be chosen by using the
getChildrenWithName(QName) methods. The getChildWithName(Qname) method
returns the first child that matches the given QName and
getChildrenWithName(QName) returns a collection containing all the matching
children. The advantage of these iterators is that they won't build the whole
object structure at once, until its required.</p>
<!-- Special section -->
<p class="special"></p>
<table width="100%">
<tbody>
<tr>
<td><img src="images/OM005.gif" alt="Remember this" width="35"
height="57"/></td>
<td class="special-td">All iterator implementations internally stay one
step ahead of their apparent location to provide the correct value
for the hasNext() method. This hidden advancement can build elements
that are not intended to be built at all. Hence these iterators are
recommended only when caching is not a concern.</td>
<td></td>
</tr>
</tbody>
</table>
<!-- End of special section -->
<a name="serializer"></a>
<h3>Serializer</h3>
<p>OM can be serialized either as the pure object model or the pull event
stream. The serialization uses a XMLStreamWriter object to write out the
output and hence, the same serialization mechanism can be used to write
different types of outputs (such as text, binary, etc.).</p>
<p>A caching flag is provided by OM to control the building of the in-memory
OM. The OMNode has two methods, serializeAndConsume and serialize. When
serializeAndConsume is called the cache flag is reset and the serializer does
not cache the stream. Hence, the object model will not be built if the cache
flag is not set.</p>
<p>The serializer serializes namespaces in the following way:</p>
<ol>
<li>When a namespace that is in the scope but not yet declared is
encountered, it will then be declared.</li>
<li>When a namespace that is in scope and already declared is encountered,
the existing declarations prefix is used.</li>
<li>When the namespaces are declared explicitly using the elements
declareNamespace() method, they will be serialized even if those
namespaces are not used in that scope.</li>
</ol>
<p>Because of this behavior, if a fragment of the XML is serialized, it will
also be <i>namespace qualified</i> with the necessary namespace
declarations.</p>
<p>Here is an example that shows how to write the output to the console, with
reference to the earlier code sample- <a href="#list1">Code Listing 1</a>
that created a SOAP envelope.</p>
<div align="left">
<p><b>Code Listing 8</b></p>
</div>
<source><pre class="code">XMLStreamWriter writer = XMLOutputFactory.newInstance().createXMLStreamWriter(System.out);
//dump the output to console with caching
envelope.serialize(writer);
writer.flush();</pre></source>
<p>or simply</p>
<pre class="code">System.out.println(root.toStringWithConsume()); </pre>
<p>The above mentioned features of the serializer forces a correct
serialization even if only a part of the OM tree is serialized. The following
serializations show how the serialization mechanism takes the trouble to
accurately figure out the namespaces. The example is from <a
href="#list6">Code Listing 6</a> which creates a small OM programmatically.
Serialization of the root element produces the following:</p>
<pre class="xml">&lt;x:root xmlns:x="bar" xmlns:y="bar1"&gt;&lt;x:foo&gt;&lt;y:yuck&gt;blah&lt;/y:yuck&gt;&lt;/x:foo&gt;&lt;/x:root&gt;</pre>
<p>However, serialization of only the foo element produces the following:</p>
<pre class="xml">&lt;x:foo xmlns:x="bar"&gt;&lt;y:yuck xmlns:y="bar1"&gt;blah&lt;/y:yuck&gt;&lt;/x:foo&gt;</pre>
<p>Note how the serializer puts the relevant namespace declarations in
place.</p>
<a name="code"></a>
<h3>Complete Code for the OM based Document Building and Serialization</h3>
<p>The following code segment shows how to use the OM for completely building
a document and then serializing it into text pushing the output to the
console. Only the important sections are shown here. The complete program
listing can be found in the <a href="#appendix">Appendix</a>.</p>
<div align="left">
<p><b>Code Listing 9</b></p>
</div>
<source><pre class="code">//create the parser
XMLStreamReader parser = XMLInputFactory.newInstance().createXMLStreamReader(new FileInputStream(file));
//create the builder
StAXOMBuilder builder = new StAXOMBuilder(parser);
//get the root element (in this case the envelope)
OMElement documentElement = builder.getDocumentElement();
//dump the out put to console with caching
System.out.println(documentElement.toStringWithConsume()); </pre></source>
<a name="advanced"></a>
<h2>Advanced Operations with OM</h2>
<a name="omnavigator"></a>
<h3>Use of the OMNavigator for Traversal</h3>
<p>OM provides a utility class to navigate the OM structure. The navigator
provides an in-order traversal of the OM tree up to the last-built node. The
Navigator has two states called the navigable state and the completion state.
Since the navigator provides the navigation starting from an OMElement, it is
deemed to have completed the navigation when the starting node is reached
again. This state is known as the completion state. Once the navigator has
reached the complete status its navigation is done and it cannot proceed.</p>
<p>It is possible that the OM tree does not get built completely when it is
navigated. The navigable status shows whether the tree structure is
navigable. When the navigator is complete it is not navigable anymore.
However, it is possible for a navigator to become non-navigable without being
complete. The following code sample shows how the navigator should be used
and handled using its states.</p>
<div align="left">
<p><b>Code Listing 10</b></p>
</div>
<source><pre class="code">//Create a navigator
OMNavigator navigator = new OMNavigator(envelope);
OMNode node = null;
while (navigator.isNavigable()) {
node = navigator.next();
}</pre></source>
<a name="accesspull"></a>
<h3>Accessing the Pull Parser</h3>
<p>OM is tightly integrated with StAX and the
getXMLStreamReader()/getXMLStreamReaderWithoutCaching() methods in the
OMElement provides a XMLStreamReader object. This XMLStreamReader instance
has a special capability of switching between the underlying stream and the
OM object tree if the cache setting is off. However, this functionality is
completely transparent to the user. This is further explained in the
following paragraphs.</p>
<p>OM has the concept of caching, and OM is the actual cache of the events
fired. However, the requester can choose to get the pull events from the
underlying stream rather than the OM tree. This can be achieved by getting
the pull parser with the cache off. If the pull parser was obtained without
switching off cache, the new events fired will be cached and the tree
updated. This returned pull parser will switch between the object structure
and the stream underneath, and the users need not worry about the differences
caused by the switching. The exact pull stream the original document would
have provided would be produced even if the OM tree was fully or partially
built. The getXMLStreamReaderWithoutCaching() method is very useful when the
events need to be handled in a pull based manner without any intermediate
models. This makes such operations faster and efficient.</p>
<!-- Special section -->
<p class="special"></p>
<table width="100%">
<tbody>
<tr>
<td><img src="images/OM005.gif" alt="Remember this" width="35"
height="57"/></td>
<td class="special-td">For consistency reasons once the cache is
switched off it cannot be switched on again.</td>
<td></td>
</tr>
</tbody>
</table>
<!-- End of special section -->
<a name="summary"></a>
<h2>Summary</h2>
<p>This is meant to be a small yet comprehensive introduction to AXIOM. AXIOM
however, is a lot more than what is described in this tutorial. Readers are
welcome to explore AXIOM, especially it's capabilities to handle binary
content.</p>
<a name="appendix"></a>
<h2>Appendix</h2>
<a name="1"></a>
<h3>Program Listing for Complete OM - Build and Serialize</h3>
<source><pre class="code">import org.apache.axiom.om.OMElement;
import org.apache.axiom.om.impl.builder.StAXOMBuilder;
import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamReader;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
public class TestOMBuilder {
/**
* Pass the file name as an argument
* @param args
*/
public static void main(String[] args) {
try {
//create the parser
XMLStreamReader parser = XMLInputFactory.newInstance().createXMLStreamReader(new FileInputStream(args[0]));
StAXOMBuilder builder = new StAXOMBuilder(parser);
//get the root element
OMElement documentElement = builder.getDocumentElement();
//dump the out put to console with caching
System.out.println(documentElement.toStringWithConsume());
} catch (XMLStreamException e) {
e.printStackTrace();
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
}</pre></source>
<a name="links"></a>
<h3>Links</h3>
For basics in XML
<ul>
<li><a
href="http://www-128.ibm.com/developerworks/xml/newto/index.html">Developerworks
Introduction to XML</a></li>
<li><a
href="http://www.bearcave.com/software/java/xml/xmlpull.html">Introduction
to Pull parsing</a></li>
<li><a
href="http://today.java.net/pub/a/today/2006/07/20/introduction-to-stax.html">Introduction
to StAX</a></li>
</ul>
</body>
</document>