繁体   English   中英

Stax XMLStreamReader 检查下一个事件而不向前移动

[英]Stax XMLStreamReader check for the next event without moving ahead

我有一个包含许多事件的大型XML文件。 我想unmarshal它们。 因为它是一个大文件,所以我想一个一个地unmarshal组它们,这样整个文件就不会存储在 memory 中。它适用于某些事件,但由于它无法将 map 转换为特定的 class 而失败,因为它已经存在在接下来的活动中。

注意:我知道XMLEventReader但他们中的大多数人都提到它效率不高 memory 因此我尝试使用XMLStreamReader并完成此操作。

以下是包含事件的示例XML文件:

<?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>

我有 3 个对应于它们的类,它们将用于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
}

以下是我的Main class,它将用于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();
        }
    }
}

我面临的问题是 class BC之间的区别。

  1. 我需要检查传入的localName是否为extension
  2. 如果它是extension那么我需要检查下一个元素localName是否也是extension
  3. 如果是, class C如果不是, class B
  4. 因为在Step-2中我已经移动到streamreader.next()并且如果该元素不是extension那么它无法将 map 转换为class B因为我已经移动到next()元素并且它没有整个 class。

我正在寻找一些可以执行以下操作的解决方案:

  1. 如果第二次验证中的元素不是extension ,则将 go 返回到前一个元素,然后将整个 class 分配给class B
  2. 进行检查时将streamReader分配给tempreader ,以便您在tempreader中前进。 但这也失败了。

有没有办法将 go 返回到 stream 中的前一个元素,否则我该如何解决这个问题? 我希望我能够提供一个完整的解释。

stream 中的“返回”意味着某种 memory,因此坚持使用内存效率最高的工具是没有意义的。

XMLEventReader可以轻松处理这个问题:

public class Main {

    public static void main(String args[]) throws Exception {
        Unmarshaller aUnmarshaller = JAXBContext.newInstance(A.class).createUnmarshaller();
        Unmarshaller bUnmarshaller = JAXBContext.newInstance(B.class).createUnmarshaller();
        Unmarshaller cUnmarshaller = JAXBContext.newInstance(C.class).createUnmarshaller();
        try (InputStream input = Main.class.getResourceAsStream("InputEPCISEvents.xml")) {
            XMLEventReader reader = XMLInputFactory.newInstance().createXMLEventReader(input);
            while (reader.hasNext()) {
                XMLEvent event = reader.peek();
                if (event.isStartElement()) {
                    switch (event.asStartElement().getName().getLocalPart()) {
                        case "a" -> System.out.println(aUnmarshaller.unmarshal(reader));
                        case "b" -> System.out.println(bUnmarshaller.unmarshal(reader));
                        case "c" -> System.out.println(cUnmarshaller.unmarshal(reader));
                    }
                }
                reader.next();
            }
        }
    }

    @XmlAccessorType(XmlAccessType.FIELD)
    static class ABC {
        String name;
        String age;

        public String toString() {
            return getClass().getSimpleName() + "{name='" + name + "', age='" + age + "}";
        }
    }
    @XmlRootElement static class A extends ABC {}
    @XmlRootElement static class B extends ABC {}
    @XmlRootElement static class C extends ABC {}
}

Output:

C{name='CName', age='CAge}
B{name='BName', age='BAge}
A{name='AName', age='AAge}
B{name='BName', age='BAge}

顺便说一句,您的 XML需要包含在父元素中,因为它包含多个根元素。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM