Unmarshalling XML to three lists of different objects using STAX Parser Unmarshalling XML to three lists of different objects using STAX Parser xml xml

Unmarshalling XML to three lists of different objects using STAX Parser


You can use Declarative Stream Mapping (DSM) stream parsing library to easily convert complex XML to java class. It uses StAX to parse XML.

I skip getting notes tag and add a field inside class_x_object tags for demostration.

Here is the XML:

<?xml version="1.0" encoding="utf-8"?><root>    <notes />    <category_alpha>        <list_a>            <class_a_object>                <fieldA>A1</fieldA>            </class_a_object>            <class_a_object>                <fieldA>A2</fieldA>            </class_a_object>            <class_a_object>                <fieldA>A3</fieldA>            </class_a_object>        </list_a>        <list_b>            <class_b_object>                <fieldB>B1</fieldB>            </class_b_object>            <class_b_object>                <fieldB>B2</fieldB>            </class_b_object>            <class_b_object>                <fieldB>B3</fieldB>            </class_b_object>        </list_b>    </category_alpha>    <category_beta>        <class_c_object>          <fieldC>C1</fieldC>        </class_c_object>        <class_c_object>          <fieldC>C2</fieldC>        </class_c_object>        <class_c_object>          <fieldC>C3</fieldC>        </class_c_object>    </category_beta></root>

First of all, you must define the mapping between XML data and your class fields in yaml or JSON format.

Here are the mapping definitions:

result:        type: object   path: /root      fields:     listOfA:       type: array       path: .*class_a_object  # path is regex       fields:          fieldOfA:            path: fieldA     listOfB:       type: array       path: .*class_b_object       fields:          fieldOfB:            path: fieldB      listOfC:       type: array       path: .*class_c_object       fields:          fieldOfC:            path: fieldC 

Java class that you want to deserialize:

public class Root {    public List<A> listOfA;    public List<B> listOfB;    public List<C> listOfC;    public static class A{        public String fieldOfA;    }    public static class B{        public String fieldOfB;    }    public static class C{        public String fieldOfC;    }}   

Java Code to parse XML:

DSM dsm=new DSMBuilder(new File("path/to/mapping.yaml")).setType(DSMBuilder.TYPE.XML).create(Root.class);Root root =  (Root)dsm.toObject(xmlFileContent);// write root object as jsondsm.getObjectMapper().writerWithDefaultPrettyPrinter().writeValue(System.out, object);

Here is output:

{  "listOfA" : [ {"fieldOfA" : "A1"}, {"fieldOfA" : "A2"}, {"fieldOfA" : "A3"} ],  "listOfB" : [ {"fieldOfB" : "B1"}, {"fieldOfB" : "B2"}, "fieldOfB" : "B3"} ],  "listOfC" : [ {"fieldOfC" : "C1"}, {"fieldOfC" : "C2"}, {"fieldOfC" : "C3"} ]}

UPDATE:

As I understand from your comment, you want to read big XML file as a stream. and process data while you are reading the file.

DSM allows you to do process data while you are reading XML.

Declare three different functions to process partial data.

FunctionExecutor processA=new FunctionExecutor(){            @Override            public void execute(Params params) {                Root.A object=params.getCurrentNode().toObject(Root.A.class);                // process aClass; save to db. call service etc.            }        };FunctionExecutor processB=new FunctionExecutor(){            @Override            public void execute(Params params) {                Root.B object=params.getCurrentNode().toObject(Root.B.class);                // process aClass; save to db. call service etc.            }        };FunctionExecutor processC=new FunctionExecutor(){            @Override            public void execute(Params params) {                Root.C object=params.getCurrentNode().toObject(Root.C.class);                // process aClass; save to db. call service etc.            }        };

Register function to DSM

 DSMBuilder builder = new DSMBuilder(new File("path/to/mapping.yaml")).setType(DSMBuilder.TYPE.XML);       // register function        builder.registerFunction("processA",processA);        builder.registerFunction("processB",processB);        builder.registerFunction("processC",processC);        DSM dsm= builder.create();        Object object =  dsm.toObject(xmlContent);

change Mapping file to call registered function

result:        type: object   path: /root      fields:     listOfA:       type: object       function: processA  # when 'class_a_object' tag closed processA function will be executed.       path: .*class_a_object  # path is regex       fields:          fieldOfA:            path: fieldA     listOfB:       type: object       path: .*class_b_object       function: processB# register function       fields:          fieldOfB:            path: fieldB      listOfC:       type: object       path: .*class_c_object       function: processC# register function       fields:          fieldOfC:            path: fieldC 


You could use Java Architecture for XML binding JAXB and Unmarshall using the POJO classes as mentioned below.

Create POJO classes first (I have taken few nodes from your XML file and created the POJO. You can do the similar for the rest). Below is the XML I considered.

<?xml version="1.0" encoding="utf-8"?><root>    <category_alpha>        <list_a>            <class_a_object></class_a_object>            <class_a_object></class_a_object>            <class_a_object></class_a_object>            <class_a_object></class_a_object>        </list_a>        <list_b>            <class_b_object></class_b_object>            <class_b_object></class_b_object>            <class_b_object></class_b_object>            <class_b_object></class_b_object>        </list_b>    </category_alpha></root>

Below are the POJO classes for Root, category_alpha, list_a, list_b, class_a_object and class_b_object

import java.util.List;import javax.xml.bind.annotation.XmlAccessType;import javax.xml.bind.annotation.XmlAccessorType;import javax.xml.bind.annotation.XmlElement;import javax.xml.bind.annotation.XmlRootElement;@XmlRootElement(name = "root")@XmlAccessorType (XmlAccessType.FIELD)public class Root {    @XmlElement(name = "category_alpha")    private List<CategoryAlpha> categoryAlphaList = null;    public List<CategoryAlpha> getCategoryAlphaList() {        return categoryAlphaList;    }    public void setCategoryAlphaList(List<CategoryAlpha> categoryAlphaList) {        this.categoryAlphaList = categoryAlphaList;    }}

Import the similar java imports to the above class here in the following classes.

@XmlRootElement(name = "category_alpha")@XmlAccessorType (XmlAccessType.FIELD)public class CategoryAlpha {    @XmlElement(name = "list_a")    private List<ListAClass> list_a_collectionlist = null;    @XmlElement(name = "list_b")    private List<ListBClass> list_b_collectionlist = null;    public List<ListAClass> getList_a_collectionlist() {        return list_a_collectionlist;    }    public void setList_a_collectionlist(List<ListAClass> list_a_collectionlist) {        this.list_a_collectionlist = list_a_collectionlist;    }    public List<ListBClass> getList_b_collectionlist() {        return list_b_collectionlist;    }    public void setList_b_collectionlist(List<ListBClass> list_b_collectionlist) {        this.list_b_collectionlist = list_b_collectionlist;    }}@XmlRootElement(name = "list_a")@XmlAccessorType (XmlAccessType.FIELD)public class ListAClass {    @XmlElement(name = "class_a_object")    private List<ClassAObject> classAObjectList = null;    public List<ClassAObject> getClassAObjectList() {        return classAObjectList;    }    public void setClassAObjectList(List<ClassAObject> classAObjectList) {        this.classAObjectList = classAObjectList;    }}@XmlRootElement(name = "list_b")@XmlAccessorType (XmlAccessType.FIELD)public class ListBClass {    @XmlElement(name = "class_b_object")    private List<ClassBObject> classBObjectList = null;    public List<ClassBObject> getClassBObjectList() {        return classBObjectList;    }    public void setClassBObjectList(List<ClassBObject> classBObjectList) {        this.classBObjectList = classBObjectList;    }}@XmlRootElement(name = "class_a_object")@XmlAccessorType (XmlAccessType.FIELD)public class ClassAObject {}@XmlRootElement(name = "class_b_object")@XmlAccessorType (XmlAccessType.FIELD)public class ClassBObject {}

Here is the Main class

import java.io.File;import javax.xml.bind.JAXBContext;import javax.xml.bind.JAXBException;import javax.xml.bind.Unmarshaller;public class UnmarshallMainClass {    public static void main(String[] args) throws JAXBException {        JAXBContext jaxbContext = JAXBContext.newInstance(Root.class);        Unmarshaller jaxbUnmarshaller = jaxbContext.createUnmarshaller();        // This root object contains all the list of objects you are looking for        Root emps = (Root) jaxbUnmarshaller.unmarshal( new File("sample.xml") );    }}

By using the getters in the root object and other objects you can retrieve the list of all objects inside the root similar like below.

List<CategoryAlpha> categoryAlphaList = emps.getCategoryAlphaList();


I have created a parser for provided example.https://github.com/sbzDev/stackoverflow/tree/master/question56087924

import com.thoughtworks.xstream.annotations.XStreamAlias;import java.util.List;@XStreamAlias("root")public class Root {    String notes;    @XStreamAlias("category_alpha")    CategoryAlpha categoryAlpha;    @XStreamAlias("category_beta")    List<C> listC;    static class CategoryAlpha {        @XStreamAlias("list_a")        List<A> listA;        @XStreamAlias("list_b")        List<B> listB;    }    @XStreamAlias("class_a_object")    static class A {    }    @XStreamAlias("class_b_object")    static class B {    }    @XStreamAlias("class_c_object")    static class C {    }}

Parser:

import com.thoughtworks.xstream.XStream;public class SampleRootParser {    public Root parse(String xmlContent){        XStream xstream = new XStream();        xstream.processAnnotations(Root.class);        return  (Root)xstream.fromXML(xmlContent);    }}

Maybe you can provide actual XML and expected result?