JAXB能否以块的forms解析大型XML文件

我需要解析可能很大的XML文件,其中的模式已经在几个XSD文件中提供给我,因此XML绑定非常受欢迎。 我想知道我是否可以使用JAXB以块的forms解析文件,如果是,那么如何。

因为代码很重要,所以这里有一个PartialUnmarshallerPartialUnmarshaller一个大文件读入块中。 它可以这样使用new PartialUnmarshaller(stream, YourClass.class)

 import javax.xml.bind.JAXBContext; import javax.xml.bind.JAXBException; import javax.xml.bind.Unmarshaller; import javax.xml.stream.*; import java.io.InputStream; import java.util.List; import java.util.NoSuchElementException; import java.util.stream.Collectors; import java.util.stream.IntStream; import static javax.xml.stream.XMLStreamConstants.*; public class PartialUnmarshaller { XMLStreamReader reader; Class clazz; Unmarshaller unmarshaller; public PartialUnmarshaller(InputStream stream, Class clazz) throws XMLStreamException, FactoryConfigurationError, JAXBException { this.clazz = clazz; this.unmarshaller = JAXBContext.newInstance(clazz).createUnmarshaller(); this.reader = XMLInputFactory.newInstance().createXMLStreamReader(stream); /* ignore headers */ skipElements(START_DOCUMENT, DTD); /* ignore root element */ reader.nextTag(); /* if there's no tag, ignore root element's end */ skipElements(END_ELEMENT); } public T next() throws XMLStreamException, JAXBException { if (!hasNext()) throw new NoSuchElementException(); T value = unmarshaller.unmarshal(reader, clazz).getValue(); skipElements(CHARACTERS, END_ELEMENT); return value; } public boolean hasNext() throws XMLStreamException { return reader.hasNext(); } public void close() throws XMLStreamException { reader.close(); } void skipElements(int... elements) throws XMLStreamException { int eventType = reader.getEventType(); List types = asList(elements); while (types.contains(eventType)) eventType = reader.next(); } } 

这在用户指南中有详细说明 。 来自http://jaxb.java.net/的JAXB下载包含一个如何一次解析一个块的示例。

当文档很大时,通常是因为文档中有重复的部分。 也许这是一个包含大量订单项的采购订单,或者它可能是包含大量日志条目的XML日志文件。

这种XML适用于块处理; 主要思想是使用StAX API,运行循环,并单独解组各个块。 你的程序作用于一个块,然后扔掉它。 通过这种方式,您只能在内存中保留最多一个块,这样您就可以处理大型文档。

有关如何执行此操作的更多信息,请参阅JAXB RI分发中的streaming-unmarshalling示例和partial-unmarshalling示例。 流式解组示例的优势在于它可以处理任意嵌套级别的块,但它需要您处理推送模型— JAXB unmarshaller将“推送”新块并且您需要正确处理它们那里。

相比之下,部分解组示例在拉模型中工作(这通常使处理更容易),但是这种方法在重复部分以外的数据绑定部分中具有一些限制。

Yves Amsellem的答案相当不错,但只有当所有元素的类型完全相同时才有效。 否则你的unmarshall会抛出exception,但读者已经消耗了这些字节,所以你将无法恢复。 相反,我们应该遵循Skaffman的建议并查看JAXB jar中的示例。

解释它是如何工作的:

  1. 创建一个JAXB unmarshaller。
  2. 向unmarshaller添加一个侦听器以拦截相应的元素。 这是通过“黑客”ArrayList来完成的,以确保元素在被解组后不会存储在内存中。
  3. 创建一个SAX解析器。 这是流媒体发生的地方。
  4. 使用unmarshaller为SAX解析器生成处理程序。
  5. 流!

我将解决方案修改为通用*。 但是,它需要一些反思。 如果不行,请查看JAXB jar中的代码示例。

ArrayListAddInterceptor.java

 import java.lang.reflect.Field; import java.util.ArrayList; public class ArrayListAddInterceptor extends ArrayList { private static final long serialVersionUID = 1L; private AddInterceptor interceptor; public ArrayListAddInterceptor(AddInterceptor interceptor) { this.interceptor = interceptor; } @Override public boolean add(T t) { interceptor.intercept(t); return false; } public static interface AddInterceptor { public void intercept(T t); } public static void apply(AddInterceptor interceptor, Object o, String property) { try { Field field = o.getClass().getDeclaredField(property); field.setAccessible(true); field.set(o, new ArrayListAddInterceptor(interceptor)); } catch (Exception e) { throw new RuntimeException(e); } } } 

Main.java

 public class Main { public void parsePurchaseOrders(AddInterceptor interceptor, List files) { try { // create JAXBContext for the primer.xsd JAXBContext context = JAXBContext.newInstance("primer"); Unmarshaller unmarshaller = context.createUnmarshaller(); // install the callback on all PurchaseOrders instances unmarshaller.setListener(new Unmarshaller.Listener() { public void beforeUnmarshal(Object target, Object parent) { if (target instanceof PurchaseOrders) { ArrayListAddInterceptor.apply(interceptor, target, "purchaseOrder"); } } }); // create a new XML parser SAXParserFactory factory = SAXParserFactory.newInstance(); factory.setNamespaceAware(true); XMLReader reader = factory.newSAXParser().getXMLReader(); reader.setContentHandler(unmarshaller.getUnmarshallerHandler()); for (File file : files) { reader.parse(new InputSource(new FileInputStream(file))); } } catch (Exception e) { throw new RuntimeException(e); } } } 

*此代码尚未经过测试,仅用于说明目的。