我想知道如何懒惰地阅读一个不适合Java内存的大型XML文件。我们假设文件格式正确,我们不必先通过检查。有人知道如何用Java做到这一点吗?
这是我的假文件(真实文件是一个50+ Gb的维基百科转储):
<pages>
<page>
<text> some data ....... </text>
</page>
<page>
<text> MORE DATA ........ </text>
</page>
</pages>
我正在尝试使用一个应该能够执行此操作的XML库,但它将整个内容加载到内存中&gt;:O
DOMParser domParser = new DOMParser();
//This is supposed to make it lazy-load the file, but it's not working
domParser.setFeature("http://apache.org/xml/features/dom/defer-node-expansion", true);
//Library says this needs to be set to use defer-node-expansion
domParser.setProperty("http://apache.org/xml/properties/dom/document-class-name", "org.apache.xerces.dom.DocumentImpl");
//THIS IS LOADING THE WHOLE FILE
domParser.parse(new InputSource(wikiXMLBufferedReader));
Document doc = domParser.getDocument();
NodeList pages = doc.getElementsByTagName("page");
for(int i = 0; i < pages.getLength(); i++) {
Node pageNode = pages.item(i);
//do something with page nodes
}
有谁知道怎么做?或者在尝试使用这个特定的Java XML库时我做错了什么?
感谢。
答案 0 :(得分:3)
您应该在Java中查看SAX解析器。构建DOM解析器是为了读取整个XML,加载到内存中,并从中创建Java对象。 SAX解析器串行解析XML文件并使用基于事件的机制来处理数据。看看差异here。
Here's a link到SAX教程。希望它有所帮助。
答案 1 :(得分:1)
如果您准备购买Saxon-EE许可证,那么您可以发出简单查询“copy-of(// page)”,执行选项设置为启用流式传输,它将返回一个迭代器一系列树,每个树都以一个页面元素为根;当你推进迭代器时,将获取每个树,并在完成它时进行垃圾收集。 (假设你真的想用Java进行处理;当然,你也可以在XQuery或XSLT中进行处理,这可能会为你节省很多代码。)
如果你有更多的时间而不是钱,并想要一个家庭酿造解决方案,那么编写一个SAX过滤器,接受来自XML解析器的解析事件并将它们发送到DocumentBuilder;每次为页面元素点击startElement事件时,都要打开一个新的DocumentBuilder;当通知相应的endElement事件时,抓取DocumentBuilder构建的树,并将其传递给Java应用程序进行处理。