XSL 3.0流式拆分大XML遇XTSE3430错误求排查
XSL 3.0流式拆分大XML文件报错排查
问题场景
需要拆分10GB级的大型XML文件,XML结构如下:
<?xml version="1.0" encoding="UTF-8"?> <Book> <Header>...</Header> <Entry>...</Entry> <Entry>...</Entry> <Entry>...</Entry> <Entry>...</Entry> </Book>
使用的XSL代码:
<xsl:stylesheet version="3.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:mode streamable="yes" on-no-match="shallow-copy"/> <xsl:template match="/"> <xsl:apply-templates select="Book"> <xsl:with-param name="header" select="/Book/Header"/> <xsl:with-param name="top-level-element" select="name(/*[1])"/> </xsl:apply-templates> </xsl:template> <xsl:template match="Book"> <xsl:param name="top-level-element"/> <xsl:param name="header"/> <xsl:result-document href="{concat(position(),'.xml')}" method="xml"> <xsl:element name="{$top-level-element}"> <xsl:value-of select="$header"/> <xsl:iterate select="Entry"> <xsl:apply-templates select="."/> </xsl:iterate> </xsl:element> </xsl:result-document> </xsl:template> <xsl:template match="Entry"> <xsl:copy-of select="."/> </xsl:template> </xsl:stylesheet>
执行转换时报错:
Error on line 6 column 29 XTSE3430 Template rule is not streamable * Operand {Book/Header} of {xsl:apply-templates} selects streamed nodes in a context that allows arbitrary navigation (line 8) * The result of the template rule can contain streamed nodes
错误原因
- 违反流式单向导航规则:在根节点模板中,执行
<xsl:apply-templates select="Book"/>后,又通过select="/Book/Header"从根节点回溯导航到Header节点,流式处理要求必须沿文档顺序单向前进,不允许回溯或随意跳转节点。 - 流式节点传递无效:将流式状态的Header节点作为参数传递给Book模板,流式节点无法被保留和重复访问,会直接破坏流处理逻辑。
修复方案
调整逻辑,在Book节点模板内部直接捕获并缓存Header节点(用xsl:copy-of将流式节点转为可复用的内存节点),再处理Entry节点,避免跨模板传递流式节点:
<xsl:stylesheet version="3.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:mode streamable="yes" on-no-match="shallow-copy"/> <xsl:template match="/"> <xsl:apply-templates select="Book"/> </xsl:template> <xsl:template match="Book"> <!-- 缓存Header为非流式节点,支持后续复用 --> <xsl:variable name="header-copy" select="copy-of(Header)"/> <xsl:variable name="top-level-element" select="name(.)"/> <!-- 生成包含所有Entry的拆分文件 --> <xsl:result-document href="output.xml" method="xml"> <xsl:element name="{$top-level-element}"> <xsl:copy-of select="$header-copy"/> <xsl:iterate select="Entry"> <xsl:copy-of select="."/> </xsl:iterate> </xsl:element> </xsl:result-document> </xsl:template> </xsl:stylesheet>
按数量拆分Entry的扩展方案
如果需要按固定数量拆分Entry(比如每1000个Entry生成一个文件),可以结合流式兼容的xsl:for-each-group实现:
<xsl:template match="Book"> <xsl:variable name="header-copy" select="copy-of(Header)"/> <xsl:variable name="top-level-element" select="name(.)"/> <!-- 每1000个Entry分为一组,生成独立文件 --> <xsl:for-each-group select="Entry" group-adjacent="(position() - 1) idiv 1000"> <xsl:result-document href="output-{position()}.xml" method="xml"> <xsl:element name="{$top-level-element}"> <xsl:copy-of select="$header-copy"/> <xsl:copy-of select="current-group()"/> </xsl:element> </xsl:result-document> </xsl:for-each-group> </xsl:template>
注:
group-adjacent按位置分组的方式符合流式处理的单向遍历要求,不会触发导航违规。
内容的提问来源于stack exchange,提问作者Mark
相关产品推荐
相关产品推荐

