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 json
dsm.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