I have a large XML
file that consists of many events. I would like to unmarshal
them. As it's a large file, I would like to unmarshal
them one by one so the whole file is not stored in memory. It works for some events but fails for some due to the fact that it's unable to map to a particular class as it's already in the next event.
Note: I am aware of the XMLEventReader
but most of them have mentioned it as not very memory efficient so I am trying to use XMLStreamReader
and accomplish this.
Following is the sample XML
file that contains the events:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<extension>
<extension>
<c>
<name>CName</name>
<age>CAge</age>
</c>
</extension>
</extension>
<extension>
<b>
<name>BName</name>
<age>BAge</age>
</b>
</extension>
<a>
<name>AName</name>
<age>AAge</age>
</a>
<extension>
<b>
<name>BName</name>
<age>BAge</age>
</b>
</extension>
I have 3 classes corresponding to them which will be used for unmarshalling
:
@XmlRootElement(name = "a")
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "a", propOrder = {"name","age"})
public class A
{
private String name;
private String age;
//Getter, Setter and other constructors
}
@XmlRootElement(name = "extension")
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "extension", propOrder = {"name","age"})
public class B
{
@XmlPath("b/name/text()")
private String name;
@XmlPath("b/age/text()")
private String age;
//Getter, Setter and other constructors
}
@XmlRootElement(name = "extension")
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "extension", propOrder = {"name","age"})
public class C
{
@XmlPath("extension/c/name/text()")
private String name;
@XmlPath("extension/c/age/text()")
private String age;
//Getter, Setter and other constructors
}
Following is my Main
class which will be used for unmarshalling
:
public class Main{
private Unmarshaller unmarshaller = null;
private JAXBContext jaxbContext = null;
public void unmarshaller(InputStream xmlStream) throws IOException, XMLStreamException, JAXBException {
final XMLInputFactory xmlInputFactory = XMLInputFactory.newInstance();
final XMLStreamReader streamReader = xmlInputFactory.createXMLStreamReader(xmlStream);
//Navigate to next and start of the XML Elements
streamReader.next();
//Read Until the end of the file
while (streamReader.hasNext()) {
//Check if the element is "extension" if so its Class B or C
if (streamReader.isStartElement() && streamReader.getLocalName().equalsIgnoreCase("extension")) {
//Check if the next element also has "extension" if so its Class C
//This is IMPORTANT step for mapping b/w Class B & C which is confusing me
streamReader.next();
if (streamReader.isStartElement() && streamReader.getLocalName().equalsIgnoreCase("extension")) {
//If there is 2 extension tag then its Class C
classSpecifier(C.class);
final C cInfo = unmarshaller.unmarshal(streamReader, C.class).getValue();
System.out.println(cInfo);
}else{
//If there is no "extension" tag then its Class B
//THIS IS WHERE ITS FAILING: IF ITS NOT CLASS C THEN IT WOULD COME HERE BUT SINCE I HAVE
//ALREADY MOVED TO NEXT ELEMENT TO CHECK IF ITS "extension" ITS UNABLE TO MAP THE WHOLE CLASS TO CLASS B
classSpecifier(B.class);
final B bInfo = unmarshaller.unmarshal(streamReader, B.class).getValue();
System.out.println(bInfo);
}
}else if(streamReader.isStartElement() && streamReader.getLocalName().equalsIgnoreCase("a")){
//If there is no "extension" then its class A
classSpecifier(A.class);
final A aInfo = unmarshaller.unmarshal(streamReader, A.class).getValue();
System.out.println(aInfo);
}
}
}
//Method to initialize the JAXBContext and Unmarshaller based on the incoming eventType
private void classSpecifier(Class eventTypeClass) throws JAXBException {
this.jaxbContext = JAXBContext.newInstance(eventTypeClass);
unmarshaller = jaxbContext.createUnmarshaller();
}
public static void main(String args[]){
try{
InputStream xmlStream = Main.class.getClassLoader().getResourceAsStream("InputEPCISEvents.xml");
unmarshaller(xmlStream);
} catch (Exception e) {
System.out.println(e);
e.printStackTrace();
}
}
}
The problem I am facing is the differentiating between class B
and C
.
- I need to check if the incoming
localName
isextension
. - If it's
extension
then I need to check if the next elementlocalName
is alsoextension
. - If so then it's
class C
if not thenclass B
. - Since in
Step-2
I have already moved tostreamreader.next()
and if the element is notextension
then its unable to map it toclass B
as I have already moved tonext()
element and it does not have the whole class.
I am looking for some solutions where I can do the following:
- If the element in the 2nd verification is not
extension
then go back to the previous element then assign the whole class toclass B
. - Assign the
streamReader
totempreader
when making a check so that you will be advancing intempreader
. But this also failing.
Is there a way to go back to the previous element in a stream or else how can I tackle this issue? I hope I was able to provide a complete explanation.
XMLEventReader
but many of the articles have mentioned that it's not very memory efficient. So still looking for the answer any suggestion would be really helpful. – Fogbow