Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
239 views
in Technique[技术] by (71.8m points)

java - Stax XMLStreamReader check for the next event without moving ahead

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.

  1. I need to check if the incoming localName is extension.
  2. If it's extension then I need to check if the next element localName is also extension.
  3. If so then it's class C if not then class B.
  4. Since in Step-2 I have already moved to streamreader.next() and if the element is not extension then its unable to map it to class B as I have already moved to next() element and it does not have the whole class.

I am looking for some solutions where I can do the following:

  1. If the element in the 2nd verification is not extension then go back to the previous element then assign the whole class to class B.
  2. Assign the streamReader to tempreader when making a check so that you will be advancing in tempreader. 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.

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)
Waitting for answers

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...