19

(以下全部用Java编写)

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

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

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

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

这是我的问题:

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

4

6 回答 6

12

税法是正确的方法。我建议看看伍德斯托克斯

于 2008-12-10T13:41:26.440 回答
7

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

该解决方案的可用性在很大程度上取决于您的扩展类实际在做什么,如果您可以控制它们的实现等等......

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

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

于 2008-12-10T13:24:46.333 回答
3

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

如果扩展类需要的部分在文件中很远,那么这可能会变得非常占用内存,'尽管如此。

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

于 2008-12-10T12:59:38.193 回答
3

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

SAXParserFactory.newInstance().newSAXParser().parse(
  new DecryptingInputStream(), 
  new MyHandler()
);
于 2008-12-10T13:57:56.580 回答
1

您可能对XOM感兴趣:

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

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

于 2008-12-10T13:21:53.907 回答
0

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

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

于 2011-03-10T21:16:24.267 回答