使用StAX和XPath读取巨大的XML文件



输入文件包含数千个XML格式的事务,大小约为10GB。需求是根据用户输入选择每个事务XML,并将其发送到处理系统。

文件

的示例内容
<transactions>
    <txn id="1">
      <name> product 1</name>
      <price>29.99</price>
    </txn>
    <txn id="2">
      <name> product 2</name>
      <price>59.59</price>
    </txn>
</transactions>

期望(技术)用户给出输入标记名称,如<txn>

我们希望提供更通用的解决方案。文件内容可能不同,用户可以使用XPath表达式(如"//transactions/txn")来选择单个事务。

这里有一些技术性的东西需要考虑

  • 文件可以在共享位置或FTP
  • 由于文件太大,我们无法在JVM中加载整个文件

我们可以在这个场景中使用StAX解析器吗?它必须将XPath表达式作为输入并挑选/选择事务XML。

寻求建议。提前感谢。

如果性能是一个重要因素,并且/或者文档大小很大(这里似乎都是这种情况),那么事件解析器(如SAX或StAX)和本机Java XPath实现之间的区别在于,后者在计算XPath表达式之前构建W3C DOM document。有趣的是,所有Java文档对象模型实现(如DOM或Axiom)都使用事件处理器(如SAX或StAX)来构建内存中的表示,因此,如果您可以只使用事件处理器,那么您就节省了构建DOM所需的内存和时间。)

正如我所提到的,JDK中的XPath实现对W3C DOM文档进行操作。通过查看com.sun.org.apache.xpath.internal.jaxp.XPathImpl,您可以在Java JDK源代码实现中看到这一点,在调用evaluate()方法之前,解析器必须首先解析源代码:

  Document document = getParser().parse( source );

在此之后,您的10GB XML将在内存中表示(加上任何开销)—可能不是你想要的。虽然您可能想要一个更"通用"的解决方案,但是您的示例XPath和XML标记似乎都相对简单,因此似乎没有真正强有力的理由使用XPath(除了编程优雅性)。对于XProc建议也是如此:这也将构建一个DOM。如果您确实需要一个DOM,您可以使用Axiom而不是W3C DOM。Axiom有一个友好得多的API,它在StAX上构建DOM,所以速度很快,并使用Jaxen实现XPath。Jaxen需要一些类型的DOM (W3C DOM、DOM4J或JDOM)。对于所有XPath实现都是如此,所以如果您并不真正需要XPath只使用事件解析器,那么建议使用。

SAX是旧的流媒体API,而StAX更新,而且速度快得多。无论是使用本地JDK StAX实现(javax.xml.stream)还是Woodstox StAX实现(在我的经验中,这要快得多),我都建议创建一个XML事件过滤器,首先匹配元素类型名称(以捕获<txn>元素)。这将创建小的事件爆发(元素、属性、文本),可以检查是否匹配用户值。在找到合适的匹配后,您可以从事件中提取必要的信息,或者通过管道将有界事件构建成一个迷你dom,如果您发现结果更容易导航的话。但是,如果标记很简单的话,这听起来可能有点过头了。

这可能是最简单、最快的方法,并且避免了构建DOM的内存开销。如果您将元素和属性的名称传递给筛选器(以便您的匹配算法是可配置的),则可以使其相对通用。

Stax和xpath是非常不同的东西。Stax允许您只向前方向解析流XML文档。Xpath允许两个方向的解析。Stax是一个非常快速的流XML解析器,但是,如果您需要xpath, java有一个单独的库。

看一下这个问题,有一个非常相似的讨论:是否有针对SAX模型的XPath处理器?

我们通常使用SAX解析器解析1GB以上的复杂XML文件,该解析器所做的正是您所描述的:它提取可以方便地使用XPATH查询的部分DOM树。

我在这里写了一篇关于它的博客——它使用的是SAX而不是StAX解析器,但可能值得一看。

这绝对是XProc与像QuiXProc (http://code.google.com/p/quixproc)这样的流并行处理实现的用例

在这种情况下,您必须使用

  <p:for-each>
    <p:iteration-source select="//transactions/txn"/>
    <!-- you processing on a small file -->
  </p:for-each>

您甚至可以用一行XProc

包装每个结果转换
  <p:wrap-sequence wrapper="transactions"/>

希望能有所帮助

处理巨大XML文件的有趣解决方案>10GB

  1. 使用ANTLR为感兴趣的部分创建字节偏移。与基于DOM的方法相比,这会节省一些内存。
  2. 使用Jaxb从字节位置读取部件

在这个SO答案中找到wikipedia转储(17GB)的示例中的详细信息https://stackoverflow.com/a/43367629/1485527

XML (STX)的流转换可能是您需要的。

您需要快速处理它还是需要快速查找数据?这些需求需要不同的方法。

对于快速读取整个数据,StAX将是OK的。

如果你需要快速查找,你可能需要加载到一些数据库,伯克利DB XML,例如

最新更新