使用 stax 和 dom 读取大型 XML 文件
Posted
技术标签:
【中文标题】使用 stax 和 dom 读取大型 XML 文件【英文标题】:Reading a big XML file using stax and dom 【发布时间】: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 解析每个条目。有这种事吗?
【问题讨论】:
【参考方案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();
另见:
Split 1GB Xml file using Java【讨论】:
谢谢,它对我很有用!我用过它,它对我帮助很大! 对我来说,在 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
【讨论】:
以上是关于使用 stax 和 dom 读取大型 XML 文件的主要内容,如果未能解决你的问题,请参考以下文章