使用xsl:try/xsl:catch时,捕获10次错误后如何终止XSLT转换?
问题描述
我们使用Saxon 12.6进行突发流式XSLT转换,处理包含多个独立<Dok>节点的XML文件。目前通过<xsl:try>/<xsl:catch>实现了单个<Dok>转换的动态错误不影响其他节点的逻辑,但希望当捕获的错误超过10次时,终止转换并重新抛出错误,找不到简单的错误计数方式。
已考虑的方案:
- 使用Java扩展函数实现计数器,但过于复杂
- 使用
<xsl:accumulator>,但未成功实现
XML文件结构示例
<Export> <Dok> <more_than_1000_lines_of_XML_to_be_transformed/> </Dok> <Dok> <more_than_1000_lines_of_XML_to_be_transformed/> </Dok> <Dok> <more_than_1000_lines_of_XML_to_be_transformed/> </Dok> <!-- typically 250 to 1000 of such <Dok> --> </Export>
简化后的XSLT代码
<xsl:template match="Export" mode="streamedDefaultMode"> <xsl:apply-template select="snapshot(Dok)" mode="grounded"/> </xsl:template> <xsl:template match="Dok" mode="grounded" priority="99"> <xsl:try> <xsl:next-match/> <xsl:catch errors="*"> <!-- here I would like something like: if ($numberOfCaughtErrors > 10) then throw "fatal error and abort the transformation --> <xsl:variable name="logMsg" select="string-join(('TrafoTransformErrorStufeCC: ', $xslerr:code, $xslerr:description,' in ', $xslerr:module, ' on line: ', $xslerr:line-number,' value: ', $xslerr:value ),' ')"/> <xsl:message select="$logMsg"/> <Dok status="error"> <Msg><xsl:value-of select="$logMsg"/></Msg> </Dok> </xsl:catch> </xsl:template> <xsl:template match="Dok" mode="grounded"> <!-- do the real transformation of a Dok -> 1000s of lines of XSLT code --> </xsl:template>
简便解决方案
方案1:修复<xsl:accumulator>实现
<xsl:accumulator>完全适配流式场景,之前未成功可能是配置细节问题,以下是正确实现方式:
定义全局累加器
在样式表顶部添加累加器,用于计数错误次数:<xsl:accumulator name="error-count" initial-value="0" streamable="yes"> <!-- 默认不修改计数 --> <xsl:accumulator-rule match="Dok" phase="end" select="$value"/> <!-- 仅在捕获到错误时,计数+1 --> <xsl:accumulator-rule match="Dok" mode="grounded" select="$value + 1" condition="$xslerr:code"/> </xsl:accumulator>在
<xsl:catch>中检查计数并终止
修改<xsl:catch>逻辑,当计数超过10时抛出致命错误:<xsl:catch errors="*"> <xsl:variable name="current-error-count" select="accumulator-before('error-count') + 1"/> <xsl:if test="$current-error-count > 10"> <!-- 直接终止转换 --> <xsl:message terminate="yes">Fatal error: Exceeded maximum allowed errors (10)</xsl:message> <!-- 或抛出结构化错误 --> <xsl:throw code="custom:too-many-errors" description="Exceeded 10 transformation errors"/> </xsl:if> <!-- 原错误日志和输出逻辑 --> <xsl:variable name="logMsg" select="string-join(('TrafoTransformErrorStufeCC: ', $xslerr:code, $xslerr:description,' in ', $xslerr:module, ' on line: ', $xslerr:line-number,' value: ', $xslerr:value ),' ')"/> <xsl:message select="$logMsg"/> <Dok status="error"> <Msg><xsl:value-of select="$logMsg"/></Msg> </Dok> </xsl:catch>启用累加器
在流式模式声明中指定使用累加器:<xsl:mode name="streamedDefaultMode" streamable="yes" use-accumulators="#all"/>
方案2:使用<xsl:iterate>维护计数
如果累加器仍有问题,可改用<xsl:iterate>遍历节点,直接维护可变计数变量(XSLT 3.0支持迭代中更新参数):
<xsl:template match="Export" mode="streamedDefaultMode"> <xsl:iterate select="snapshot(Dok)"> <xsl:param name="error-count" select="0"/> <xsl:try> <xsl:apply-templates select="." mode="grounded"/> <!-- 无错误时保持计数不变 --> <xsl:next-iteration> <xsl:with-param name="error-count" select="$error-count"/> </xsl:next-iteration> </xsl:try> <xsl:catch errors="*"> <xsl:variable name="new-error-count" select="$error-count + 1"/> <xsl:if test="$new-error-count > 10"> <xsl:throw code="custom:too-many-errors" description="Exceeded 10 transformation errors"/> </xsl:if> <!-- 原错误日志和输出逻辑 --> <xsl:variable name="logMsg" select="string-join(('TrafoTransformErrorStufeCC: ', $xslerr:code, $xslerr:description,' in ', $xslerr:module, ' on line: ', $xslerr:line-number,' value: ', $xslerr:value ),' ')"/> <xsl:message select="$logMsg"/> <Dok status="error"> <Msg><xsl:value-of select="$logMsg"/></Msg> </Dok> <!-- 更新计数后继续迭代 --> <xsl:next-iteration> <xsl:with-param name="error-count" select="$new-error-count"/> </xsl:next-iteration> </xsl:catch> </xsl:iterate> </xsl:template>
该方案逻辑更直观,无需依赖累加器配置,同样适配流式场景。
内容的提问来源于stack exchange,提问作者Reto
相关产品推荐
相关产品推荐

