【问题标题】:Can JAXB parse large XML files in chunksJAXB 可以分块解析大型 XML 文件吗
【发布时间】:2009-07-15 21:26:25
【问题描述】:

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

【问题讨论】:

    标签: java jaxb


    【解决方案1】:

    因为代码很重要,所以这里有一个PartialUnmarshaller,他将一个大文件读成块。可以这样使用new PartialUnmarshaller<YourClass>(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<T> {
        XMLStreamReader reader;
        Class<T> clazz;
        Unmarshaller unmarshaller;
    
        public PartialUnmarshaller(InputStream stream, Class<T> 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<Integer> types = asList(elements);
            while (types.contains(eventType))
                eventType = reader.next();
        }
    }
    

    【讨论】:

    • 我需要使用 XMLStreamConstants.START_DOCUMENT 等才能使其工作。
    • @MatthiasWuttke 您可以将它们添加为静态导入。导入静态 javax.xml.stream.XMLStreamConstants.*;
    • 你可能还需要 Guava 的 Ints.asList 或 java8 IntStream.of(elements).boxed().collect(Collectors.toList());
    • 您好,感谢您的回复。我正在做类似的事情,但无法为我的班级安排一些活动。如果你有机会,你能看看这个问题并提供你的观察吗? stackoverflow.com/questions/67667516/…
    【解决方案2】:

    这在user guide中有详细说明。从http://jaxb.java.net/ 下载的 JAXB 包含一个如何一次解析一个块的示例。

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

    这种 XML 适用于 块处理;主要思想是 使用 StAX API,运行一个循环,然后 解组单个块 分别地。您的程序作用于 单个块,然后将其丢弃。 这样,你只会保持在 内存中的大多数块,它允许 您可以处理大型文档。

    查看流式解组 示例和部分解组 JAXB RI 分布中的示例 有关如何执行此操作的更多信息。这 流解组示例有一个 它可以处理块的优点 任意嵌套级别,但它需要 你来处理push模型--- JAXB unmarshaller 将“推”新 大块给你,你需要 在那里处理它们。

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

    【讨论】:

    【解决方案3】:

    Yves Amsellem 的回答非常好,但只有在所有元素的类型完全相同时才有效。否则你的 unmarshall 会抛出一个异常,但是阅读器已经消耗了字节,所以你将无法恢复。相反,我们应该遵循 Skaffman 的建议并查看 JAXB jar 中的示例。

    解释它是如何工作的:

    1. 创建一个 JAXB 解组器。
    2. 为解组器添加一个侦听器以拦截适当的元素。这是通过“破解”ArrayList 来完成的,以确保元素在解组后不会存储在内存中。
    3. 创建一个 SAX 解析器。这就是流式传输发生的地方。
    4. 使用解组器为 SAX 解析器生成处理程序。
    5. 直播!

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

    ArrayListAddInterceptor.java

    import java.lang.reflect.Field;
    import java.util.ArrayList;
    
    public class ArrayListAddInterceptor<T> extends ArrayList<T> {
        private static final long serialVersionUID = 1L;
    
        private AddInterceptor<T> interceptor;
    
        public ArrayListAddInterceptor(AddInterceptor<T> interceptor) {
            this.interceptor = interceptor;
        }
    
        @Override
        public boolean add(T t) {
            interceptor.intercept(t);
            return false;
        }
    
        public static interface AddInterceptor<T> {
            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<PurchaseOrder> interceptor, List<File> 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);
            }
        }
    }
    

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

    【讨论】:

      【解决方案4】:

      我写了一个small library(在 Maven Central 上可用)来帮助读取大型 XML 文件并按块处理它们。请注意,它只能应用于具有唯一容器的文件,该容器具有数据列表(即使来自不同类型)。换句话说,您的文件必须遵循以下结构:

      <container>
         <type1>...</type1>
         <type2>...</type2>
         <type1>...</type1>
         ...
      </container>
      

      这是一个示例,其中Type1Type2、... 是文件中重复数据的 JAXB 表示:

      try (StreamingUnmarshaller unmarshaller = new StreamingUnmarshaller(Type1.class, Type2.class, ...)) {
          unmarshaller.open(new FileInputStream(fileName));
          unmarshaller.iterate((type, element) -> doWhatYouWant(element));
      }
      

      您可以在库的 GitHub 页面上找到包含详细示例的更多信息。

      【讨论】:

        猜你喜欢
        • 2011-05-09
        • 1970-01-01
        • 1970-01-01
        • 2016-05-29
        • 2019-09-23
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多