[英]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 B
和C
之间的区别。
localName
是否为extension
。extension
那么我需要检查下一个元素localName
是否也是extension
。class C
如果不是, class B
。Step-2
中我已经移动到streamreader.next()
并且如果该元素不是extension
那么它无法将 map 转换为class B
因为我已经移动到next()
元素并且它没有整个 class。我正在寻找一些可以执行以下操作的解决方案:
extension
,则将 go 返回到前一个元素,然后将整个 class 分配给class B
。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.