我的目标是将包含各种内容(大约2到15 GB)的大型单个XML文件拆分为多个XML文件,每个文件都包含某种实体类型,稍后可以由SQL数据库导入。我目前正在使用 Saxon-EE版本9.5.1.2J ,但如果它能快速可靠地完成任务,那么任何其他XSL处理器都可以。
这是我已经想到的:
<xsl:result-document>
将输出内容写入其他文件,但相同样式表中不多次使用它可以写入相同的文件(显然不是线程安全的)。<xsl:for-each-group>
with group-by显然不 streamable <xsl:stream>
只能包含一个 <xsl:iterate>
块,这没关系。但是:在迭代块中,您只能访问当前节点的属性和一个子节点(甚至<xsl:for-each>
在该节点上工作)。如果您尝试访问第二个节点的值,则会出现“SXST0060:多个子表达式消耗输入流”错误。<xsl:apply-templates>
在<xsl:stream>
内(而不是迭代)需要模式可流化(如下所示)。但是,流只能像迭代一样使用一次 - 否则你也会得到错误“SXST0060:多个子表达式消耗输入流”。
我的结论是,当前可用的XSL处理器需要使用多个<xsl:stream>
标记来写入不同的文件,这实际上意味着扫描多次的大型输入文件< strong>每个输出文件。当将不同的实体写入相同输出文件作为变通方法时,情况确实如此,因为不可能多次“使用”相同的输入流:
<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的基本技术限制阻止了这种用例。
答案 0 :(得分:2)
问题实际上很容易解决:使用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>
注意:直接在<xsl:iterate>
中放置copy-of()不起作用,您将获得大文档的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()手动组织一些数据缓冲。我 希望找到其他方式可以放松撒克逊人的限制 以便让事情变得更容易。
答案 1 :(得分:0)
“使用简单的命令行脚本更快”
我同意。您可以从脚本调用OS和商业命令行xml分割器。 他们使用快速前向读取xml解析器,消耗很少的内存,并且可以一次完成拆分。 Google用于“拆分大型xml文件”