XSLT如何为所有Item节点递归拼接带PV属性的祖先及自身PV值生成点分隔串
核心问题定位
- 第一版硬编码写法的缺陷:通过
../../@PV手动计算层级的方式完全绑定固定嵌套深度,遇到XML结构调整、深度变化时会直接取不到对应节点的PV值。比如PV为c的Item,往上数3级已经到无PV属性的root节点,就会出现开头空值、路径错位的问题。 - 第二版写法的缺陷:
- 硬编码指定了
//Item[@PV='f']单个节点,没有对文档内所有Item节点做通用遍历 - 代码里直接在每个节点的PV值后面拼接静态文本的
.,会在路径末尾生成多余的点号 - XSLT引擎会把代码缩进、换行的空白字符当成普通文本输出,导致点号后混入多余空格,最终输出成
a. d. e. f.的不符合预期的效果
- 硬编码指定了
通用实现方案
XSLT 1.0 兼容写法(适配绝大多数传统XSLT引擎)
核心思路是不再手动硬编码层级,通过轴选择当前Item对应的所有带PV属性的祖先与自身,再通过位置判断拼接分隔符,彻底避免末尾多余点号:
<table border="1"> <xsl:for-each select="//Item"> <tr> <td> <!-- 获取当前节点所有带PV属性的祖先+自身,按文档顺序从根到当前节点排列 --> <xsl:variable name="pvPathNodes" select="ancestor-or-self::*[@PV]"/> <!-- 拼接路径,仅在非第一个节点前加分隔点 --> <xsl:for-each select="$pvPathNodes"> <xsl:if test="position() != 1"> <xsl:text>.</xsl:text> </xsl:if> <xsl:value-of select="@PV"/> </xsl:for-each> </td> </tr> </xsl:for-each> </table>
注意:用<xsl:text>.</xsl:text>包裹点号,是为了隔离代码缩进、换行带来的空白字符,彻底解决输出中混进多余空格的问题。
XSLT 2.0+ 简化写法
如果运行环境支持XSLT 2.0及以上版本,可以直接使用内置的string-join函数完成拼接,不需要手动写循环判断:
<table border="1"> <xsl:for-each select="//Item"> <tr> <td> <xsl:value-of select="string-join(ancestor-or-self::*[@PV]/@PV, '.')"/> </td> </tr> </xsl:for-each> </table>
实际输出效果
两个Item节点最终会生成完全符合要求的路径值:
- PV为c的Item:
a.b.c - PV为f的Item:
a.d.e.f
这套写法完全不需要预先知道Item的嵌套深度,只要路径上的父元素带有PV属性,就可以自动完成拼接,适配任意层级的结构。
内容的提问来源于stack exchange,提问作者vanoo83
相关产品推荐
相关产品推荐

