The problem was i could not find a clear and concise tutorial explaining how to deal with XML using AXIOM. After playing around with it i figured out how to manipulate XML with AXIOM which i should say is so much better that the cumbersome code you have to deal with when manipulating with DOM or JDOM.
So following i show a simple example of how to manipulate XML with AXIOM;
First off i present the XML i will be parsing
<?xml version="1.0" encoding="utf-8" ?> <my_servers> <server> <server-name>PROD</server-name> <server-ip>xx.xx.xx.xx</server-ip> <server-port>80</server-port> <server-desc>Running A/S</server-desc> </server> <server> <server-name>PROD2</server-name> <server-ip>xx1.xx1.xx1.xx1</server-ip> <server-port>80</server-port> <server-desc>Running A/S</server-desc> </server> </my_servers>
Next i wrote a factory method to handout StaxBuilder instances depending on the XML file you pass. I have done as such so as to minimize the task of creating new StaxBuilder instances everytime.
import java.io.FileInputStream; import java.io.FileNotFoundException; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import javax.xml.stream.XMLStreamException; import org.apache.axiom.om.impl.builder.StAXOMBuilder; public class AxiomStaxBuilderFactory { private static Map<String, StAXOMBuilder> staxBuilderMap = new ConcurrentHashMap<String, StAXOMBuilder>(); /** * The factory method stores the {@link StAXOMBuilder} instance created for each XML file<br> * passed in so that we do not need to create unnecessary objects every time.<br> * An instance of {@linkplain ConcurrentHashMap} is used so as to make the<br> * instances thread safe. * * @param xmlFilePath the path of the XML file * @return an instance of the {@link StAXOMBuilder} from the cache or newly created */ public static StAXOMBuilder getAxiomBuilderForFile(String xmlFilePath) { StAXOMBuilder staxBuilder = null; if (staxBuilderMap.containsKey(xmlFilePath)) { staxBuilder = staxBuilderMap.get(xmlFilePath); } else { try { staxBuilder = new StAXOMBuilder(new FileInputStream(xmlFilePath)); staxBuilderMap.put(xmlFilePath, staxBuilder); } catch (FileNotFoundException e) { throw new AxiomBuilderException(e); } catch (XMLStreamException e) { throw new AxiomBuilderException(e); } } return staxBuilder; } }
I have used a Concurrent Hash map so that this wil work well in a multi threaded application. If your not bothered with that you might as well use a normal HashMap for better performance which in this case would be negligible. I have also used a custom exception as i did not want the user to have to handle exceptions so i wrapped the exceptions thrown to my custom run time exception. Following is that code. Nothing major just a normal extension of the RuntimeException class;
/** * This exception class wraps all exceptions thrown from the Axiom API * as the user does not need to be bound by such checked exceptions. * @author dinuka * */ public class AxiomBuilderException extends RuntimeException { /** * */ private static final long serialVersionUID = -7853903625725204661L; public AxiomBuilderException(Throwable ex) { super(ex); } public AxiomBuilderException(String msg) { super(msg); } }
Next off i have written a utility class to deal with the XML parsig. Ofcourse this is not needed but i just had it so that client calls will be much cleaner without having to deal with XML releated coding which would be abstracted by the utility class. Note -The current method only reads the root level elements passed in.
import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import javax.xml.namespace.QName; import org.apache.axiom.om.OMElement; import org.apache.axiom.om.impl.builder.StAXOMBuilder; /** * The utility class provides abstraction to users so that <br> * the user can just pass in the xml file name and the node he/she<br> * wants to access and get the values without having to bother with<br> * boilerplate xml handling info. * * @author dinuka */ public class AxiomUtil { /** * This method is used if you have for example a node with multiple children<br> * Note that this method assumes the node in query is within the root element * * @param xmlFilePath the path of the xml file * @param nodeName the node name from which you want to retrieve values * @return the list containing key value pairs containing the values of the sub elements within<br> * the nodeName passed in. */ public static List<Map<String, String>> getNodeWithChildrenValues(String xmlFilePath, String nodeName) { List<Map<String, String>> valueList = new ArrayList<Map<String, String>>(); StAXOMBuilder staxBuilder = AxiomStaxBuilderFactory.getAxiomBuilderForFile(xmlFilePath); OMElement documentElement = staxBuilder.getDocumentElement(); Iterator nodeElement = documentElement.getChildrenWithName(new QName(nodeName)); while (nodeElement.hasNext()) { OMElement om = (OMElement) nodeElement.next(); Iterator it = om.getChildElements(); Map<String, String> valueMap = new HashMap<String, String>(); while (it.hasNext()) { OMElement el = (OMElement) it.next(); valueMap.put(el.getLocalName(), el.getText()); } valueList.add(valueMap); } return valueList; } }
And finally i give to you a sample class to test out the XML parsing example i have presented to you here.
import java.util.List; import java.util.Map; /** * Test class depicting the use of Axiom parsing XML * * @author dinuka */ public class testServerConfigXML { public static void main(String argv[]) { List<Map<String, String>> values = AxiomUtil.getNodeWithChildrenValues("/home/dinuka/serverInfo.xml", "server"); for (Map<String, String> mapVals : values) { for (String keys : mapVals.keySet()) { System.out.println(keys + "=" + mapVals.get(keys)); } } } }
Thats it. If you have any queries or any improvement points you see pls do leave a comment which would be highly appreciated. Hope this helps anyone out there looking for a similar basic tutorial on AXIOM XML parsing.
Cheers
great ,., thanx mate ...
ReplyDeletecena ..
Hi,
ReplyDeleteCould you let me know the jar files that i need to use to implement the axiom based xml parsing.
regds,
deepak.
Hi Deepak,
ReplyDeleteThis jar would suffice;
http://repo1.maven.org/maven2/org/apache/ws/commons/axiom/axiom-dom/1.2.13/axiom-dom-1.2.13.bundle