首页 > 解决方案 > XML XSLT 使用 SAXON EE10.6 流式传输大型 xml 文件

问题描述

我必须将大型 xml 文件 (>5Gb) 导入 SOLR。我想先用 SAXON EE10.6 和流式 xsl 转换一个 xml 文件。我已阅读 SAXON EE10.6 应该可以,但我收到以下错误:

mytest.xsl 的第 20 行第 34 列出错:XTSE3430 模板规则不可流式传输

我不熟悉流式传输 xslt 和撒克逊语。如何让我的 xslt 适合流式传输以输出所需的 Solr 添加文档 xml。

我在这里有一个简化版本的 xml 和我使用的 xslt:https ://xsltfiddle.liberty-development.net/asoTKU

它适用于较小的 xml 文件 (<1Gb)

标签: xmlxsltsolrsaxon

解决方案


XSLT 3.0 流的规则非常复杂,教程介绍很少也无济于事。一个非常有用的资源是 Abel Braaksma 在 XML 布拉格 2014 上的演讲:https ://www.xfront.com/Transcript-of-Abel-Braaksma-talk-on-XSLT-Streaming- at-XML-Prague-2014.pdf

要记住的最重要的规则是:模板规则只能进行一次向下选择(它只有一次机会扫描后代树)。这就是您在编写时打破的规则:

<xsl:template match="node()">
   <xsl:element name="field">
      <xsl:attribute name="name">
        <xsl:value-of select="local-name()"/>
      </xsl:attribute>
      <xsl:value-of select="."/>
   </xsl:element>
   <xsl:apply-templates select="*"/>
</xsl:template>

实际上,该代码可以简化为

<xsl:template match="node()">
   <field name="{local-name()}">{.}</field>
   <xsl:apply-templates select="*"/>
</xsl:template>

但这不会影响流能力:您正在处理匹配节点的后代两次,一次获取字符串值 (.),一次将模板应用于子节点。

现在,在我看来,这个模板规则似乎只用于处理“叶元素”,即具有文本节点子节点但没有子元素的元素。如果是这种情况,那么<xsl:apply-templates select="*"/>永远不会选择任何东西:它是多余的并且可以被删除,这使得规则可流化。

您会收到另一条错误消息,即模板规则可以返回流式节点。不允许返回流式节点的原因有点微妙。它基本上使处理器无法进行数据流分析以证明流式传输是否可行。但这又是<xsl:apply-templates select="*"/>问题的原因,摆脱它可以解决问题。

您的下一个问题是关于 Property 元素的模板规则。你把它写成

   <xsl:template match="Property">
        <xsl:element name="field">
            <xsl:attribute name="name">
               <xsl:value-of select="key"/>_s</xsl:attribute>
            <xsl:value-of select="value"/>
        </xsl:element>
        <xsl:apply-templates select="Property"/>
    </xsl:template>

它简化为:

<xsl:template match="Property">
    <field name="{key}_s">{value}</field>
    <xsl:apply-templates select="Property"/>
</xsl:template>

这将做出三个向下的选择:child::keychild::valuechild::Property。在您的数据样本中,没有Property元素有一个名为 的子元素Property,因此 可能<xsl:apply-templates/>又是多余的。一个有用的技巧是将它们读入地图keyvalue

<xsl:template match="Property">
    <xsl:variable name="pair" as="map(*)">
      <xsl:map>
        <xsl:map-entry key="'key'" select="string(key)"/>
        <xsl:map-entry key="'value'" select="string(value)"/>
      </xsl:map>
    </xsl:variable>
    <field name="{$pair?key}_s">{$pair?value}</field>
</xsl:template>

这样做的原因是xsl:map(like xsl:fork) 是“一次向下选择”规则的一个例外 - 地图可以在输入的单次传递中构建。通过调用string(),我们注意不要将任何流式节点放入地图中,因此我们稍后需要的数据已经在地图中捕获,我们不需要返回流式输入文档再次读取它.

我希望这能让您对前进的道路有所了解。XSLT 中的流式处理不适合胆小的人,但如果您有 >5Gb 的输入文档,那么您就没有太多选择了。


推荐阅读