在java中解析非常大的XML文档(以及更多)

时间:2008-12-10 12:41:02

标签: java xml memory-management streaming sax

(以下所有内容均以Java编写)

我必须构建一个应用程序,它将把可能非常大的XML文档作为输入。该文档是加密的 - 不是使用XMLsec,而是使用我客户的预先存在的加密算法 - 将分三个阶段进行处理:

首先,根据上述算法对流进行解密。

其次,扩展类(由我提供的API的第三方编写)将读取文件的某些部分。读取的数量是不可预测的 - 特别是它不能保证在文件的标题中,但可能出现在XML中的任何一点。

最后,另一个扩展类(相同的交易)将输入XML细分为1..n子集文档。这些可能会在某些方面与第二个操作处理的文档部分重叠,即:我相信我需要回顾我用来处理这个对象的任何机制。

这是我的问题:

有没有办法在没有一次将整个数据读入内存的情况下执行此操作?显然我可以将解密实现为输入流过滤器,但我不确定是否可以按照我描述的方式解析XML;通过遍历,需要收集第二步的信息,然后通过倒回文档并再次传递它以将其拆分为作业,理想情况下释放文档的所有不再使用的部分他们已经过去了。

6 个答案:

答案 0 :(得分:12)

Stax是正确的方法。我建议看一下Woodstox

答案 1 :(得分:7)

这听起来像是StAX(JSR 173)的工作。 StAX是一个拉解析器,这意味着它或多或少像SAX这样的基于事件的解析器,但你可以更好地控制何时停止读取,拉动哪些元素,......

此解决方案的可用性将在很大程度上取决于您的扩展类实际执行的操作,是否可以控制其实现等...

重点是,如果文档非常大,您可能希望使用基于事件的解析器而不是基于树的,因此您不会使用大量内存。

可以从SUN(SJSXP),Codehaus或其他一些提供商处找到StAX的实现。

答案 2 :(得分:3)

我会写一个InputStream的自定义实现来解密文件中的字节,然后使用 SAX 来解析生成的XML,因为它离开了流。

SAXParserFactory.newInstance().newSAXParser().parse(
  new DecryptingInputStream(), 
  new MyHandler()
);

答案 3 :(得分:3)

您可以使用缓冲区大小非常大的BufferedInputStream,并在扩展类工作之前使用mark(),之后使用reset()

如果扩展类所需的部分距离文件很远,那么这可能会变得非常耗费内存,但是。

更通用的解决方案是编写自己的BufferedInputStream - 如果要缓冲的数据超过某个预设阈值,则缓冲到磁盘的工作方式。

答案 4 :(得分:1)

您可能会对XOM感兴趣:

  

XOM非常独特,因为它是一个   双流/基于树的API。   树中的各个节点都可以   在文档静止时处理   正在建设。启用XOM程序   操作几乎和。一样快   底层解析器可以提供数据。您   不需要等待文档   在你可以之前完全解析   开始使用它。

     

XOM非常节省内存。如果你   将整个文档读入内存,   XOM使用尽可能少的内存。   更重要的是,XOM允许您   过滤文件,因为它们是这样构建的   你不必建立部分   你不感兴趣的树   例如,您可以跳过构建文本   仅表示边界的节点   白色空间,如果有这样的白色空间   在您的申请中没有重要意义   您甚至可以处理文档片段   逐件扔掉每一件   当你完成它。 XOM一直都是   用来处理文件   千兆字节。

答案 5 :(得分:0)

查看XOM库。您要查找的示例是源代码分发的samples目录中的StreamingExampleExtractor.java。这显示了一种用于执行大型xml文档的流式解析的技术,该文档仅构建特定节点,处理它们并丢弃它们。它与sax方法非常相似,但内置了更多的解析功能,因此可以非常轻松地实现流式解析。

如果您想在更高级别工作,请查看NUX。这提供了一个高级流式xpath API,它只将数据量读入评估xpath所需的内存中。