使用单个XSL流将一个大型XML文件拆分为多个文件

我的目标是将包含各种内容(大约2到15 GB)的大型单个
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>里面< xsl:stream> (而不是迭代)需要模式可流化(如下所示).但是,流只能像迭代一样使用一次 – 否则你也会得到错误“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的基本技术限制阻止了这种用例.

最佳答案 问题实际上很容易解决:使用copy-of()可以在单个迭代块内访问节点和所有子节点(例如下面的示例中的名称):

<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)那里得到了关于这个问题的反馈:

Yes, once you make more than one downward selection, you need to
manually organize some buffering of data by using copy-of(). I’m
hoping to find other ways the restriction can be relaxed in Saxon as
well to make things a bit easier.

点赞