【问题标题】:Reading a big XML file using stax and dom使用 stax 和 dom 读取大型 XML 文件
【发布时间】:2012-03-11 21:35:35
【问题描述】:

我需要读取几个大的 (200Mb-500Mb) XML 文件,所以我想使用 StaX。 我的系统有两个模块 - 一个用于读取文件(使用 StaX);另一个模块(“解析器”模块)假设获取该 XML 的单个条目并使用 DOM 解析它。 我的 XML 文件没有特定的结构 - 所以我不能使用 JaxB。 如何将“解析器”模块传递给我希望它解析的特定条目? 例如:

<Items>
   <Item>
        <name> .... </name>
        <price> ... </price>
   </Item>
   <Item>
        <name> .... </name>
        <price> ... </price>
   </Item>
</Items>

我想使用 StaX 来解析该文件 - 但每个“项目”条目都将传递给“解析器”模块。

编辑:
再读一读之后——我想我需要一个使用流读取 XML 文件的库——但使用 DOM 解析每个条目。有这种事吗?

【问题讨论】:

    标签: java xml dom stax


    【解决方案1】:

    由于https://bugs.openjdk.java.net/browse/JDK-8016914,Blaise Doughan 的答案在干净的 java 7 和 8 中失败了

    java.lang.NullPointerException
    at com.sun.org.apache.xerces.internal.dom.CoreDocumentImpl.setXmlVersion(CoreDocumentImpl.java:860)
    at com.sun.org.apache.xalan.internal.xsltc.trax.SAX2DOM.setDocumentInfo(SAX2DOM.java:144)
    

    有趣的是:如果你使用 jaxb unmarshaller,你不会得到 NPE:

    package com.common.config;
    
    import java.io.*;
    
    import javax.xml.bind.JAXBContext;
    import javax.xml.bind.JAXBElement;
    import javax.xml.bind.Unmarshaller;
    import javax.xml.stream.*;
    
    import org.w3c.dom.*;
    
    public class Demo {
    
    
        public static void main(String[] args) throws Exception  {
            XMLInputFactory xif = XMLInputFactory.newInstance();
            XMLStreamReader xsr = xif.createXMLStreamReader(new FileReader("input.xml"));
            // Advance to root element
            xsr.nextTag(); // TODO: nextTag() can't skip DTD
            xsr.next(); // Advance to first item or EOD
    
            final JAXBContext jaxbContext = JAXBContext.newInstance();
            final Unmarshaller unm = jaxbContext.createUnmarshaller();
            while(true) {
                // previous unmarshal() already did advance to next element or whitespace
                if (xsr.getEventType() == XMLStreamReader.START_ELEMENT) {
                    JAXBElement<Object> jel = unm.unmarshal(xsr, Object.class);
                    Node domNode = (Node)jel.getValue();
                    System.err.println(domNode.getNodeName());
                } else if (!xsr.hasNext()) {
                        break;
                } else {
                    xsr.next();
                }
            }
        }
    
    }
    

    原因是:com.sun.xml.internal.bind.v2.runtime.unmarshaller.StAXConnector$1 没有实现Locator2 因此它没有getXMLVersion()

    【讨论】:

      【解决方案2】:

      您可以使用 StAX (javax.xml.stream) 解析器并将每个部分转换 (javax.xml.transform) 到 DOM 节点 (org.w3c.dom):

      import java.io.*;
      import javax.xml.stream.*;
      import javax.xml.transform.*;
      import javax.xml.transform.stax.StAXSource;
      import javax.xml.transform.dom.DOMResult;
      import org.w3c.dom.*
      
      public class Demo {
      
          public static void main(String[] args) throws Exception  {
              XMLInputFactory xif = XMLInputFactory.newInstance();
              XMLStreamReader xsr = xif.createXMLStreamReader(new FileReader("input.xml"));
              xsr.nextTag(); // Advance to statements element
      
              TransformerFactory tf = TransformerFactory.newInstance();
              Transformer t = tf.newTransformer();
              while(xsr.nextTag() == XMLStreamConstants.START_ELEMENT) {
                  DOMResult result = new DOMResult();
                  t.transform(new StAXSource(xsr), result);
                  Node domNode = result.getNode();
              }
          }
      
      }
      

      另见:

      【讨论】:

      • 谢谢,它对我很有用!我用过它,它对我帮助很大!
      • 对我来说,在 Java 8 中,t.transform() 行抛出了 TransformerException:javax.xml.transform.TransformerException:无法转换 javax.xml.transform 类型的源。 stax.StAXSource.
      • 我将 Apache Xalan 作为依赖项,它提供了自己的 TransformerFactory。解决此问题的一种方法是明确指定 TransformerFactory 类:TransformerFactory transformerFactory = TransformerFactory.newInstance( "com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl", null );
      【解决方案3】:

      你可以试试 JLibs 的XMLDog

      它使用 SAX 评估 xml 文档上的 xpath(即不将整个 xml 加载到内存中)。 并在节点被命中时返回 dom 节点。

      因此,您可以在胖 xml 文档上评估 xpath /Items/Item。在解析每个 Item 节点时,您将收到通知。您可以处理当前的Item dom节点,然后继续。

      因此它适用于评估大型文档上的 xpaths

      【讨论】:

        猜你喜欢
        • 2011-11-05
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-12-23
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多