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

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

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

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

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

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

这是我的问题:

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

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

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

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

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

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

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

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

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

我会编写一个InputStream的自定义实现,它解密文件中的字节,然后使用SAX解析生成的XML。

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

您可能对XOM感兴趣:

XOM非常独特,因为它是一个双流/基于树的API。 在构建文档时,可以处理树中的各个节点。 使XOM程序的运行速度几乎与底层解析器可以提供数据一样快。 在开始使用文档之前,您无需等待文档完全解析。

XOM非常有效。 如果将整个文档读入内存,XOM将尽可能少地使用内存。 更重要的是,XOM允许您在构建文档时对其进行过滤,这样您就不必构建您不感兴趣的树的部分。例如,您可以跳过构建仅表示边界空白区域的文本节点,如果这样的空白区域在您的应用程序中不重要 您甚至可以一件一件地处理文档,并在完成后丢弃每件文件。 XOM已用于处理大小为千兆字节的文档。

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

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