我的目标是将包含各种内容(大约 2 到 15 GB)的大型单个 XML 文件拆分为多个 XML 文件,每个文件包含特定的实体类型,例如稍后可以通过 SQL 数据库导入该实体类型。我目前使用Saxon-EE 版本 9.5.1.2J,但任何其他 XSL 处理器只要它能够快速可靠地完成工作,就可以了。
这是我已经弄清楚的:
<xsl:result-document>
它将输出写入不同的文件,但不能在同一样式表中多次使用它来写入同一文件(显然不是线程安全的)。<xsl:for-each-group>
使用 group-by 显然是不可流式传输的<xsl:stream>
只能包含一个 <xsl:iterate>
块,这是可以的。但是:在该迭代块内,您只能访问当前节点和一个子节点的属性(甚至<xsl:for-each>
适用于该一个节点)。如果您尝试访问第二个节点的值,则会收到“SXST0060:多个子表达式消耗输入流”错误。<xsl:apply-templates>
inside <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>
Run Code Online (Sandbox Code Playgroud)
使用解释的且更复杂的命令行脚本从大型 XML 文件中提取不同实体的速度更快,因此相比之下,XSLT 变得缓慢且无用:(
我希望有一个基于 XSLT 3.0 的解决方案能够按预期工作,而无需多次扫描输入文件?我不认为 XSLT 的基本技术限制会阻止这种用例。
这个问题实际上很容易解决:使用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>
Run Code Online (Sandbox Code Playgroud)
注意:直接放入 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() 手动组织一些数据缓冲。我希望在撒克逊找到其他放宽限制的方法,让事情变得更容易一些。