XSLT apply-template相同子树匹配处理规则及模板重叠规避问题
核心原因说明
你观察到的“重复子树跳过遍历”不是XSLT的遍历优化逻辑,是XSLT 1.0的标准模板冲突处理规则:当多个模板的匹配路径完全一致、优先级属性相同时,XSLT处理器只会调用最后声明的那一个模板,其余同优先级的匹配模板会被直接丢弃,不会执行。
避免规则重叠、安全扩展模板的方法
1. 优先用规范设计规避冲突
你当前的校验场景最适合用按校验维度划分mode的方案,不同校验规则完全隔离,不会出现匹配冲突:
<xsl:template match="/"> <Errors> <!-- 依次执行不同维度的校验,规则完全隔离 --> <xsl:apply-templates mode="count-check"/> <xsl:apply-templates mode="unique-check"/> <xsl:apply-templates mode="format-check"/> </Errors> </xsl:template> <!-- 数量校验维度:水果数量大于5 --> <xsl:template match="food/fruits" mode="count-check"> <xsl:if test="count(fruit) <=5"> <Error code="FRUIT_COUNT_ERR" message="水果数量不能少于5个"/> </xsl:if> <!-- 继续遍历当前节点的子节点,触发同维度下的子节点校验规则 --> <xsl:apply-templates mode="count-check"/> </xsl:template> <!-- 唯一性校验维度:水果名称唯一 --> <xsl:template match="food/fruits" mode="unique-check"> <xsl:if test="count(fruit) != count(fruit[not(. = preceding-sibling::fruit)])"> <Error code="FRUIT_DUPLICATE_ERR" message="水果名称不能重复"/> </xsl:if> <xsl:apply-templates mode="unique-check"/> </xsl:template>
这种方案下,新增校验规则只要新增对应mode的模板即可,无需担心和其他维度的规则出现匹配重叠。
如果不想拆分mode,也可以直接把同一节点的多个校验逻辑合并到同一个匹配模板中:
<xsl:template match="food/fruits"> <!-- 校验1:数量大于5 --> <xsl:if test="count(fruit) <=5"> <Error code="FRUIT_COUNT_ERR" message="水果数量不能少于5个"/> </xsl:if> <!-- 校验2:名称唯一 --> <xsl:if test="count(fruit) != count(fruit[not(. = preceding-sibling::fruit)])"> <Error code="FRUIT_DUPLICATE_ERR" message="水果名称不能重复"/> </xsl:if> <!-- 新增校验直接往下追加即可 --> <xsl:apply-templates/> </xsl:template>
2. 静态检测重复匹配规则
可以通过以下两种方式提前发现重复的模板规则:
- 用XSLT处理器自带的冲突检测参数,比如Saxon运行时加
-warn:on参数,Xalan运行时加-EDump参数,处理器会自动输出模板匹配冲突的警告,包括完全相同的匹配路径、优先级相同的重叠匹配。 - 自己写简单的预处理脚本,提取所有
xsl:template节点的match和mode属性组合,只要(match, mode)二元组重复,就直接抛出“请合并相同匹配路径的模板”的提示,从规则入口处拦截非法定义。
3. 特殊场景下的优先级配置
如果确实需要保留同一节点、同一mode下的多个匹配模板,可以给每个模板设置不同的priority属性(数值越大优先级越高),同时每个模板末尾调用<xsl:apply-templates select="." mode="#current" priority="-1"/>来链式触发低优先级的同匹配模板,不过这种方案维护成本较高,不推荐大规模使用。
重复模板警告触发方案
主流XSLT处理器都自带模板冲突检测能力,开启对应参数后,只要出现两个同优先级、同mode、匹配相同节点的模板,就会自动输出警告提示你合并规则。如果是自研的校验系统,也可以在加载XSLT规则库的阶段,先通过XPath扫描所有xsl:template的属性,发现重复的(match, mode)组合就直接抛出自定义的“请合并两个模板规则”错误。
内容的提问来源于stack exchange,提问作者DonBaka
相关产品推荐
相关产品推荐

