In this example I would like to show how unmarshall XML into a POJO using HierarchicalStreamReader. I will introduce how to use XStream and a custom XML-to-POJO converter, describe a case study and develop an application to show the solution.
Purchase History Example
Consider the following XML. It represents represents a purchase history of a customer, that has purchased different items during a week:
<PURCHASE> <ITEM> <SUBITEM> <CODE>PDCT-001</CODE> <NAME>Product-1</NAME> <DAY5>1.75</DAY5> <TOTAL>1.75</TOTAL> </SUBITEM> <SUBITEM> <CODE>PDCT-002</CODE> <NAME>Product-2</NAME> <DAY7>1.00</DAY7> <DAY6>1.75</DAY6> <TOTAL>2.75</TOTAL> </SUBITEM> <SUBITEM> <CODE>PDCT-003</CODE> <NAME>Product-3</NAME> <DAY7>4.50</DAY7> <DAY6>2.00</DAY6> <DAY5>3.00</DAY5> <DAY4>2.25</DAY4> <TOTAL>11.75</TOTAL> </SUBITEM> <SUBITEM> <CODE>PDCT-004</CODE> <NAME>Product-4</NAME> <DAY7>3.50</DAY7> <DAY6>3.75</DAY6> <DAY5>2.75</DAY5> <DAY1>3.75</DAY1> <TOTAL>13.75</TOTAL> </SUBITEM> <REFERENCE>9571-EDGDFG-DGSNJE-1837</REFERENCE> <GRANDTOTAL>30</GRANDTOTAL> </ITEM> </PURCHASE>
To unmarshall the XML, I created three POJOs to represent the hierarchy of XML elements. The domain objects Purchase, Item and SubItem are involved. The Purchase has an Item, that has a collection of SubItem objects. My customer XML converter will transform customer’s purchase history from XML into a domain model using the POJOs.
I am not going to describe the full structure of the POJOs here, as I want to concentrate on talking about the XML converter . The source code as Eclipse project is attached to the current article, so you can download it.
I use XStream, which is a simple open-source Java library for serialize objects to and from XML. The following is the unit test case code snippet that show how I register my custom converter that is used for XML unmarshalling:
XStream xstream = new XStream(new DomDriver());
xstream.alias("PURCHASE", Purchase.class);
xstream.registerConverter(new PurchaseConverter());
Purchase purchase = (Purchase) xstream.fromXML(xmlContent);
I created an alias for Purchase that matches XML root element and registered my custom converter. The converter is a specific converter that knows how to handle this particular XML example, so it is not a generic solution. The converter code as follows:
package asia.javabeans.streamreader.util; import asia.javabeans.streamreader.domain.Purchase; import asia.javabeans.streamreader.domain.SubItem; import com.thoughtworks.xstream.converters.Converter; import com.thoughtworks.xstream.converters.MarshallingContext; import com.thoughtworks.xstream.converters.UnmarshallingContext; import com.thoughtworks.xstream.io.HierarchicalStreamReader; import com.thoughtworks.xstream.io.HierarchicalStreamWriter; /** * Current class represents converter for a Purchase XML. * * @author alexander.zagniotov * */ final public class PurchaseConverter implements Converter { private static final int NUM_OF_DAYS = 7; public void marshal(Object value, HierarchicalStreamWriter writer, MarshallingContext context) { } public Object unmarshal(HierarchicalStreamReader reader, UnmarshallingContext context) { Purchase purchase = new Purchase(); //while there are unread XML element children and the reader has not reached the closing PURCHASE element while (!(reader.getNodeName().equals(XmlElements.PURCHASE) && !reader .hasMoreChildren())) { if (reader.hasMoreChildren()) { reader.moveDown(); // Move down to ITEM element // Process child SUBITEM elements of the parent ITEM if (reader.getNodeName().equals(XmlElements.ITEM)) { iterateThroughItemSubitems(reader, XmlElements.ITEM, XmlElements.SUBITEM, purchase); } } //store value of REFERENCE element in a POJO if (reader.getNodeName().equals(XmlElements.REFERENCE)) { purchase.setReference(reader.getValue()); } reader.moveUp(); // Move back up to PURCHASE element } return purchase; } private Purchase iterateThroughItemSubitems( HierarchicalStreamReader reader, String parentNameName, String childNodeName, Purchase purchase) { boolean hasSubitemNodes = true; while (reader.getNodeName().equals(parentNameName) && hasSubitemNodes) { reader.moveDown(); // Move down to SUBITEM element // We have SUBITEM element that we can read if (reader.getNodeName().equals(childNodeName)) { purchase = iterateThroughSubitemChildren(reader, childNodeName, purchase); } else { // No more children SUBITEM elements to read break; } reader.moveUp(); // Move back up to parent ITEM element continue; } return purchase; } private Purchase iterateThroughSubitemChildren( HierarchicalStreamReader reader, String nodeName, Purchase purchase) { boolean hasChildrenNodes = true; SubItem subItem = new SubItem(); while (reader.getNodeName().equals(nodeName) && hasChildrenNodes) { // Move down to the children of current SUBITEM element reader.moveDown(); if (reader.getNodeName().equals(XmlElements.CODE)) { String subItemCode = reader.getValue(); subItem.setCode(subItemCode); reader.moveUp(); // Move back up to parent SUBITEM continue; } if (reader.getNodeName().equals(XmlElements.NAME)) { String subItemName = reader.getValue(); subItem.setName(subItemName); reader.moveUp(); // Move back up to parent SUBITEM continue; } if (reader.getNodeName().startsWith(XmlElements.DAY)) { for (int i = 1; i <= NUM_OF_DAYS; i++) { String xmlDay = XmlElements.DAY + i; if (reader.getNodeName().equals(xmlDay)) { double hours = Double.parseDouble(reader.getValue()); subItem.addToDailyHours(xmlDay, hours); purchase.addToGrandTotal(hours); break; } } reader.moveUp(); // Move back up to parent SUBITEM continue; } if (reader.getNodeName().equals(XmlElements.TOTAL)) { hasChildrenNodes = false; // This is the last child of parent SUBITEM String itemTotalStr = reader.getValue(); subItem.setItemTotal(Double.parseDouble(itemTotalStr)); purchase.getItem().addSubItem(subItem); reader.moveUp(); // Move back up to parent SUBITEM continue; } } return purchase; } @SuppressWarnings("unchecked") public boolean canConvert(Class clazz) { return clazz.equals(Purchase.class); } }
As you can see, the key to operate a stream reader is to move it down to a child element for reading and back up to a parent element. Each time I move the reader down, it moves to an element that has not being read yet.
Conclusion
I presented a simple example where I show how to unmarshall XML into Java POJOs using HierarchicalStreamReader and XStream. The source code is attached.
I hope this tutorial was helpful to some of you,
Regards
Convert XML into POJO using HierarchicalStreamReader – http://b2l.me/amvkgd