XSLT转换时position()函数始终返回1的问题解决
position() 恒返回1的核心原因
position()函数的取值规则是返回当前正在被遍历的节点序列中,当前上下文节点的相对序号,结果完全由当前遍历的节点集大小、节点在集内的位置决定,和XML文档的绝对层级没有关联。
出现恒为1的问题,本质都是两类代码逻辑错位:
- 遍历目标和预期不一致
基础测试用例中,原xsl:for-each的select路径是main/collection,示例XML中这个路径仅匹配到1个<collection>节点,循环本身仅会执行1次。循环内部定义的$itemName变量取的是当前collection下所有<item>节点的集合,预期的逐item遍历逻辑根本没有触发;循环内的position()因为始终在唯一的collection节点上执行,自然恒等于1。 - 上下文意外切换
复杂业务场景中常见的触发原因:如果在xsl:for-each的内部代码块中,后续调用模板、或者通过其他XPath把当前上下文切换到新的单节点序列,此时再取position()就会因为新序列只有1个节点,固定返回1。
另外如果xsl:template的match路径写错,导致每次进入mode="edges"的模板时,descriptorList/person/personTerm/personText路径仅能匹配到1个节点,循环同样只会执行一次,position()自然恒为1。
正确获取位置索引的实现方案
要拿到正确的节点序号,核心是让xsl:for-each的遍历目标精准对应需要取序号的节点本身,不要隔层取节点集:
- 基础测试场景修正
把for-each的select路径从main/collection改成直接遍历每个item节点,即可拿到正确的位置值:<xsl:for-each select="main/collection/item"> <!-- 此时遍历的序列是3个item节点,position()会依次返回1/2/3 --> <xsl:variable name="counter" select="position()" /> <tr> <td><xsl:value-of select="$counter"/></td> <td><xsl:value-of select="."/></td> </tr> </xsl:for-each> - 复杂业务场景修正
原有for-each的遍历目标本身是descriptorList/person/personTerm/personText,只要满足两个条件即可拿到正确序号:- 确认
xsl:template的match路径能正确定位到<descriptorList>节点,保证person/personTerm/personText路径可以一次性匹配到Paul、John、Peter对应的所有personText节点,不会因为路径写错导致每次仅匹配单个节点。 position()的取值要写在for-each节点的直接上下文层级,不要把取position的逻辑放到xsl:call-template或者其他会切换上下文的节点内部。正确写法示例:
<xsl:template match="personSet/descriptorList" mode="edges"> <xsl:for-each select="person/personTerm/personText"> <!-- 此处直接在for-each上下文取position(),会自动返回遍历序列的1/2/3序号 --> <xsl:variable name="counter" select="position()" /> <xsl:call-template name="templateToProcess"> <xsl:with-param name="hierarchy" select="$counter" /> <xsl:with-param name="name" select="." /> </xsl:call-template> </xsl:for-each> </xsl:template> - 确认
补充说明
XSLT的变量确实是单次赋值后不可重新赋值的常量,完全不需要手动编写自增逻辑——只要节点遍历逻辑正确,position()会自动根据当前遍历进度生成对应序号,不需要额外维护计数器。
内容的提问来源于stack exchange,提问作者Patrick
相关产品推荐
相关产品推荐

