4

我的目标是将包含各种内容(大约 2 到 15 GB)的大型单个 XML 文件拆分为多个 XML 文件,每个 XML 文件都包含特定的实体类型,例如稍后可以通过 SQL 数据库导入。我目前正在使用Saxon-EE 版本 9.5.1.2J,但任何其他 XSL 处理器只要它能够快速可靠地完成这项工作就可以了。

这是我已经弄清楚的:

  • Saxon似乎是 XSLT 3.0 的事实上的标准处理器,而 Raptor XML 服务器似乎是另一种(更昂贵的)选择。其他 XSL 处理器通常只支持 XSLT 1.0。
  • 可以使用XSLT 3.0 流处理大文件,因此不需要将整个文件放入内存中。注意:此功能仅在 Saxon Enterprise Edition 中可用。
  • 您可以使用<xsl:result-document>将输出写入不同的文件,但不能在同一个样式表中多次使用它来写入同一个文件(显然不是线程安全的)。
  • <xsl:for-each-group>与 group-by 显然是不可流式的
  • <xsl:stream>只能包含一个 <xsl:iterate>块,没关系。但是:在该迭代块中,您只能访问当前节点和一个子节点的属性(甚至<xsl:for-each>可以在该节点上工作)。如果您尝试访问第二个节点的值,则会收到“SXST0060:多个子表达式消耗输入流”错误。
  • <xsl:apply-templates>inside <xsl:stream>(而不是迭代)需要模式流式处理(如下所示)。但是,与 iterate 一样,该流只能使用一次 - 否则您还会收到错误“SXST0060:多个子表达式使用输入流”。

我的结论是,当前可用的 XSL 处理器需要使用多个<xsl:stream>标签来写入不同的文件,这实际上意味着为每个输出文件多次扫描大型输入文件。这甚至是正确的,当将不同的实体写入同一个输出文件作为一种解决方法时,因为不可能多次“使用”同一个输入流:

<xsl:mode name="s" streamable="yes"/>

<xsl:template match="/">
    <xsl:stream href="input.xml">
        <xsl:apply-templates mode="s" select="content/articles"/>
    </xsl:stream>

    <xsl:stream href="input.xml">
        <xsl:apply-templates mode="s" select="content/articles/article/authors"/>
    </xsl:stream>
</xsl:template>

使用解释的和更复杂的命令行脚本从大型 XML 文件中提取不同实体的速度更快——因此相比之下 XSLT 变得缓慢且无用:(

我希望有一个基于 XSLT 3.0 的解决方案可以按预期工作而无需多次扫描输入文件?我没有看到 XSLT 的基本技术限制阻止了这种用例。

4

2 回答 2

3

这个问题实际上很容易解决:使用copy-of()使您能够访问name单个迭代块中的节点和所有子节点(例如下面的示例):

<xsl:stylesheet version="3.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output method="xml"
                omit-xml-declaration="no"
                encoding="UTF-8"
                indent="yes"/>

    <xsl:template match="/">
        <xsl:stream href="input.xml">
            <resultset xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
                <xsl:iterate select="content/articles/article">
                    <xsl:for-each select="copy-of()/.">
                        <xsl:apply-templates select="."/>
                        <xsl:apply-templates select="authors/author"/>
                    </xsl:for-each>
                </xsl:iterate>
            </resultset>
        </xsl:stream>
    </xsl:template>

    <xsl:template match="article">
        ...
    </xsl:template>

    <xsl:template match="author">
        ...
    </xsl:template>
</xsl:stylesheet>

注意:将 copy-of() 直接放入<xsl:iterate>不起作用,对于大型文档,您将收到 OutOfMemoryError。不需要模式流式传输。

Saxon 可以通过这种方式在我的 MacBook Air 上每分钟处理大约 1 GB 的 XML。现在,我仍在将所有实体写入同一个输出文件,但 MySQL 可以过滤哪些节点被导入到每个表中(http://dev.mysql.com/doc/refman/5.5/en/load-xml. html ),因此解决方法不是主要问题。如果您知道如何将输出写入交替输出文件,请告诉我。

我还直接从 Michael Kay (Saxonica) 那里得到了关于这个问题的反馈:

是的,一旦你做了多个向下的选择,你需要使用 copy-of() 手动组织一些数据缓冲。我希望找到其他可以在撒克逊放宽限制的方法,以使事情变得更容易一些。

于 2013-09-25T13:30:12.340 回答
0

“使用简单的命令行脚本更快”

我同意。它们是您可以从脚本调用的操作系统和商业命令行 xml 拆分器。他们使用一种快速前向读取的 xml 解析器,该解析器消耗很少的内存并且可以一次性完成拆分。谷歌“拆分大型 xml 文件”

于 2013-09-24T14:32:59.287 回答