优化XSLT for-each中checkUniqueValues模板的性能
高效实现XSLT的checkUniqueValues模板
核心思路
无需遍历所有节点,而是通过XPath直接判断值的唯一性,或者在遍历过程中一旦发现不同值立即终止,避免无意义的全量遍历。
方案1:XSLT 2.0+(推荐,代码简洁高效)
利用XSLT 2.0及以上的原生特性,借助处理器优化实现快速判断:
实现代码
<xsl:template name="checkUniqueValues"> <xsl:param name="nodeParentName"/> <xsl:param name="nodeName"/> <!-- 定位所有目标节点 --> <xsl:variable name="targetNodes" select="//*[local-name() = $nodeParentName]/*[local-name() = $nodeName]"/> <xsl:choose> <!-- 无目标节点时返回True(可根据业务调整) --> <xsl:when test="not($targetNodes)">True</xsl:when> <!-- 判断所有节点值是否与第一个节点值一致 --> <xsl:when test="$targetNodes = $targetNodes[1]">True</xsl:when> <xsl:otherwise>False</xsl:otherwise> </xsl:choose> </xsl:template>
性能优势
- 底层由XSLT处理器优化,会在发现第一个不匹配节点时停止比较,比手动
for-each高效得多。 - 也可通过
distinct-values简化判断:count(distinct-values($targetNodes)) <= 1,效果一致。
方案2:XSLT 1.0(兼容旧版本处理器)
XSLT 1.0无distinct-values,可通过递归模板提前终止遍历或key去重实现:
递归终止版(推荐,提前退出)
<xsl:template name="checkUniqueValues"> <xsl:param name="nodeParentName"/> <xsl:param name="nodeName"/> <!-- 内部递归参数,默认初始化 --> <xsl:param name="nodes" select="//*[local-name() = $nodeParentName]/*[local-name() = $nodeName]"/> <xsl:param name="baseValue" select="$nodes[1]"/> <xsl:choose> <xsl:when test="not($nodes)">True</xsl:when> <!-- 找到不同值,直接返回False --> <xsl:when test="$nodes[1] != $baseValue">False</xsl:when> <!-- 只剩最后一个节点,返回True --> <xsl:when test="count($nodes) = 1">True</xsl:when> <!-- 递归遍历剩余节点 --> <xsl:otherwise> <xsl:call-template name="checkUniqueValues"> <xsl:with-param name="nodes" select="$nodes[position() > 1]"/> <xsl:with-param name="baseValue" select="$baseValue"/> </xsl:call-template> </xsl:otherwise> </xsl:choose> </xsl:template>
调用时只需传入前两个参数:
<xsl:call-template name="checkUniqueValues"> <xsl:with-param name="nodeParentName" select="'ParentNode'"/> <xsl:with-param name="nodeName" select="'TargetNode'"/> </xsl:call-template>
Key去重版
<xsl:key name="valueKey" match="//*[local-name() = $nodeParentName]/*[local-name() = $nodeName]" use="."/> <xsl:template name="checkUniqueValues"> <xsl:param name="nodeParentName"/> <xsl:param name="nodeName"/> <xsl:variable name="targetNodes" select="//*[local-name() = $nodeParentName]/*[local-name() = $nodeName]"/> <xsl:variable name="uniqueCount" select="count($targetNodes[generate-id() = generate-id(key('valueKey', .)[1])])"/> <xsl:value-of select="$uniqueCount <= 1"/> </xsl:template>
额外性能优化建议
- 避免使用
//全文档搜索,调用模板时尽量传入上下文节点,用相对路径缩小查找范围。 - 若模板多次调用,提前缓存目标节点集,避免重复查找。
- 开启XSLT处理器的优化选项(如Saxon的优化配置),进一步提升大型XML的处理速度。
内容的提问来源于stack exchange,提问作者RamAlx
相关产品推荐
相关产品推荐

