如何用XSLT 2/3选择版本≤输入值且最接近的topic节点?
问题描述
我有一个包含多个带version属性的topic节点的XML,版本为数值型。存在相同id、不同版本的topic节点。需要选择其中一个topic节点:版本小于等于输入变量且最接近该变量的节点。
示例XML:
<topics> <topic id="1" version="6"/> <topic id="1" version="3"/> <topic id="1" version="1"/> </topics>
匹配规则:
- 输入变量为7时,选择
version="6"的topic; - 输入为6时选
version="6"的; - 输入为5时选
version="3"的。
目前通过枚举版本的xsl:choose实现,但版本已有16个且后续会增加,希望替换该枚举方案(使用Saxon,支持XSLT 2和3)。
当前枚举实现代码:
<xsl:variable name="ver" select="7"/> <xsl:choose> <xsl:when test="//topic[@version = $ver]"> <xsl:copy-of select="//topic[@version = $ver]"> </xsl:when> <xsl:when test="//topic[@version = $ver -1]"> <xsl:copy-of select="//topic[@version = $ver -1]"> </xsl:when> ... </xsl:choose>
解决方案
XSLT 2.0 通用实现
无需枚举版本,通过过滤+排序直接定位目标节点:
<xsl:variable name="target-ver" select="7"/> <!-- 筛选当前id下版本≤目标值的节点,按版本降序排序后取第一个 --> <xsl:copy-of select=" (//topic[@id='1'] [number(@version) <= $target-ver] order by number(@version) descending )[1] "/>
XSLT 3.0 简洁实现
借助max()函数直接获取符合条件的最大版本,再匹配对应节点:
<xsl:variable name="target-ver" select="7"/> <xsl:variable name="max-valid-ver" select=" max(//topic[@id='1']/number(@version)[. <= $target-ver]) "/> <xsl:copy-of select="//topic[@id='1' and @version = $max-valid-ver]"/>
如果需要批量处理多个不同id的分组场景,XSLT 3.0的分组语法更高效:
<xsl:variable name="target-ver" select="7"/> <xsl:for-each-group select="//topic" group-by="@id"> <xsl:variable name="max-ver" select="max(current-group()/number(@version)[. <= $target-ver])"/> <xsl:copy-of select="current-group()[@version = $max-ver]"/> </xsl:for-each-group>
关键说明
- 用
number()转换version属性为数值,避免字符串排序的错误(比如"10"字符串排序会排在"2"前面); - 核心逻辑统一为:先过滤版本≤目标值的节点,再从中选版本最大的,彻底摆脱枚举依赖;
- Saxon对上述XSLT 2/3语法完全支持,无需额外配置。
内容的提问来源于stack exchange,提问作者Hobbes
相关产品推荐
相关产品推荐

