Open SourceGoing Beyond XML Manipulation with Apache AXIOM

Going Beyond XML Manipulation with Apache AXIOM

Developer.com content and product recommendations are editorially independent. We may make money when you click on links to our partners. Learn More.

With the popularity of Apache Axis2, Axiom also got a good attention. One of the cool things is that you don’t need to have Axis2 to use Axiom, you can download Axiom separately and use specifically. Axiom is used in a number of Apache project and other project in addition to Apache Axis2. In the first part of the article I discussed Pull and Push parser technology as well as gave an Axiom overview. I also showed how to create Axiom in various ways and how to work with attributes and namespaces. So if you followed the first part, then you have good background knowledge to follow this article. If you have not followed the first part then it is recommended that you read it before continuing.

Tree Navigation

Navigating the object structure can be done in the usual way by using the list of children (similar to any of the XML processing tool). However in Axiom, the child nodes are returned as an iterator. The idea of iterator is to support “Pull parsing” and ”On-Demand” building, so only the element you asked for will be read into the memory. Others will remain untouched. The following code sample shows how the children can be accessed. The children are of the type OMNode and can either be of type OMText or OMElement.

Get all Nodes

Iterator children = root.getChildren();
       while(children.hasNext()){
       OMNode node = (OMNode)children.next();
}

Get Only Elements

You can use following code to all child OMElement for a given Element, in this case you will not get any OMText element.

Iterator children = root.getChildElements();
       while(children.hasNext()){
       OMElement node = (OMElement)children.next();
}

Get Element by Name

If you know the Qname (full qualified name) of the element then you can get that from any given element as follows.

 OMElement element = root.getFirstChildWithName(new Qname("myElement"));

Get Elements by Name

If you have a list of elements with the same name, then you can get all of them by using the following command,

Iterator children = root.getChildrenWithName()

In addition to this way, every OMNode has links to its siblings. If more thorough navigation is needed the nextSibling() and PreviousSibling() methods can be used.

Serialization

Now you should have a good understanding on creating and traversing the Axiom tree, therefore next most important part is to learn how to serialize or write Axiom tree into an output stream. Axiom can be serialized either as the pure object model or the pull event stream. The serialization uses an 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.).

A caching flag is provided by Axiom to control the building of the in-memory Axiom. 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 in which case it will serialize XML stream directory to the output stream without creating object model. If the serializeAndConsume method is called, then you can serialize the axiom tree only once, since it does not build the axiom tree into memory. However, the serialize method can be called any number of times. I will cover the difference between the two later in this section.

Serialize to OutputStream

OutputStream out = new FileOutputStream(“filename”);
XMLStreamWriter writer = XMLOutputFactory.newInstance().createXMLStreamWriter(out);
//dump the output to console with caching
root.serialize(writer); 
writer.flush();

Note: You can call also call the toString() method of any given OMElement to serialize the element.

The difference between serializeAndConsume() and serialize()

Now you should try to understand the difference between the serializeAndConsume() and serialize() methods. First try to call the serialize method twice in the axiom element as shown in the following sample code.

String xmlStream = 
    "<ns:name></ns:name><ns:isbn></ns:isbn></ns:book>";
 
 //Create an input stream for the string
 ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xmlStream.getBytes());
 //create a builder. Since we want the XML as a plain XML, we can just use the plain OMBuilder
StAXBuilder builder = new StAXOMBuilder(byteArrayInputStream);
OMElement root = builder.getDocumentElement();
 root.serialize(System.out);
 root.serialize(System.out);

If you run the above sample code then you will see the following output in the console.

<ns:book xmlns_ns="axis2" type="web-services"><ns:name></ns:name><ns:isbn></ns:isbn></ns:book>
<ns:book xmlns_ns="axis2" type="web-services"><ns:name></ns:name><ns:isbn></ns:isbn></ns:book>

However, if you call serializeAndConsume() first and then call serialize(), you’ll get an exception. This is because once you have called the serializeAndConsume() method, the Axiom tree will not be built and the cache flag is reset. As such, the next time you try to call serialize you have nothing left to serialize so you will get an exception.

String xmlStream = "<ns:book xmlns_ns="axis2" type="web-services"><ns:name></ns:name><ns:isbn></ns:isbn></ns:book>";
 //Create an input stream for the string
ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xmlStream.getBytes());
//create a builder. Since we want the XML as a plain XML, we can just use the plain OMBuilder
 StAXBuilder builder = new StAXOMBuilder(byteArrayInputStream);
OMElement root = builder.getDocumentElement();
 root.serializeAndConsume(System.out);
root.serialize(System.out);

Xpath Navigation

Axiom has Xpath navigation support through Jaxen, so you can write Xpath query and invoke them in Axiom. Write an Xpath query to get an ISBN number from the book element you created. If you run the following sample then you should get “56789”as output in the console.

String xmlStream = "<book type="web-services"><name></name><isbn>56789</isbn></book>";
        
ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xmlStream.getBytes());
StAXBuilder builder = new StAXOMBuilder(byteArrayInputStream);
OMElement root = builder.getDocumentElement();
AXIOMXPath xpath = new AXIOMXPath("/book/isbn[1]");
OMElement selectedNode = (OMElement) xpath.selectSingleNode(root);
System.out.println(selectedNode.getText());

Axiom and SOAP

As discussed, Axiom was developed as XML representation mechanism in Axis2, therefore as a SOAP processing framework Axis2 needs to work with SOAP. SOAP is also XML but it has its own structure to be a special type of XML, so it is easy if you can get SOAP level API from Axiom. Axiom has built-in support for SOAP representation and navigation. You can create and navigate SOAP 1.1 and 1.2 documents easily with Axiom. When you navigate SOAP, Axiom has API to get the headers and body, therefore you do not need to get an iterator and navigate. The following two samples shows how to create SOAP 1.1 and 1.2 document easily with Axiom.

Creating SOAP 1.1 document

OMFactory factory = OMAbstractFactory.getOMFactory();
OMNamespace axis2 = factory.createOMNamespace("axis2", "ns");
OMElement root = factory.createOMElement("book", axis2);
OMAttribute type = factory.createOMAttribute("type",null,"web-services");
root.addAttribute(type);
OMElement name = factory.createOMElement("name", axis2);
OMElement isbn = factory.createOMElement("isbn", axis2);
root.addChild(name);
root.addChild(isbn);

SOAPFactory soapFactory = OMAbstractFactory.getSOAP11Factory();
 //get the default envelope
SOAPEnvelope env = soapFactory.getDefaultEnvelope();
//add the created child
env.getBody().addChild(root);
System.out.println( env);

As seen in the listing, first you create a book element as the body of the SOAP message, then create a default SOAP 1.1 envelope, and finally add the created book element as the body.

Creating SOAP 1.2 document

Creating SOAP 1.2 documents is almost the same as above except for the factory. Here you need to use 1.2 factory instead of 1.1 factory.

OMFactory factory = OMAbstractFactory.getOMFactory();
OMNamespace axis2 = factory.createOMNamespace("axis2", "ns");
OMElement root = factory.createOMElement("book", axis2);
OMAttribute type = factory.createOMAttribute("type",null,"web-services");
root.addAttribute(type);
OMElement name = factory.createOMElement("name", axis2);
OMElement isbn = factory.createOMElement("isbn", axis2);
root.addChild(name);
root.addChild(isbn);

SOAPFactory soapFactory = OMAbstractFactory.getSOAP12Factory();
 //get the default envelope
SOAPEnvelope env = soapFactory.getDefaultEnvelope();
//add the created child
env.getBody().addChild(root);
 System.out.println( env);

Summary

In the first part of the article Axiom basis and usage was covered along with some of the XML processing API of Axiom. In this part navigating Axiom was covered along with how to use Xpath expression to process Axiom, and how to serialize Axiom into an output stream. Finally, the Axiom APIs for processing SOAP were discussed. If you follow both of the articles with samples then you should be ready to work with Axiom.

Get the Free Newsletter!

Subscribe to Developer Insider for top news, trends & analysis

Latest Posts

Related Stories