XSLT1.0属性值XPath替换错误排查:同名属性不同层级路径解析异常问题
问题分析与解决方案
你的问题核心在于递归处理占位符时丢失了原始上下文节点,导致XPath路径的计算基准完全错误。
错误原因详解
当前的expand模板在替换占位符时,递归调用自身时传递的是属性值(比如../@*[name()=$name]),这会让模板的上下文从原来的元素节点(比如<slot>)直接变成这个属性节点。后续再处理其他占位符时,所有的../都是相对于这个属性节点,而非原始的元素节点,自然就会拿到错误的属性值。
举个具体的例子:处理<slot>的title属性时,原本{../../@attr3}应该基于<slot>元素向上找两层(到<desc>节点)取它的attr3值(555),但你的代码递归后上下文变成了属性节点,../../../指向了错误的层级,最后只能拿到<slot>自己的attr3值(333),导致三个占位符都被替换成了同一个值。
修正后的XSLT代码
我们需要给expand模板新增一个参数,用来保存原始的上下文节点(也就是包含当前属性的元素节点),所有XPath的计算都基于这个节点,不会因为递归而偏移:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0" xmlns:msxsl="urn:schemas-microsoft-com:xslt"> <xsl:output method="xml" indent="yes" omit-xml-declaration="yes"/> <xsl:strip-space elements="*"/> <xsl:template match="node()"> <xsl:copy> <xsl:apply-templates select="@*|node()"/> </xsl:copy> </xsl:template> <xsl:template match="@*"> <xsl:attribute name="{name()}"> <xsl:call-template name="expand"> <xsl:with-param name="text" select="."/> <xsl:with-param name="context" select=".."/> <!-- 传递属性所属的元素节点作为固定上下文 --> </xsl:call-template> </xsl:attribute> </xsl:template> <xsl:template name="expand"> <xsl:param name="text"/> <xsl:param name="context"/> <!-- 保存原始上下文节点,全程不变 --> <xsl:choose> <!-- 统一处理所有{...}格式的占位符 --> <xsl:when test="contains($text, '{') and contains(substring-after($text, '{'), '}')"> <xsl:value-of select="substring-before($text, '{')"/> <!-- 提取大括号内的完整XPath表达式 --> <xsl:variable name="xpath" select="substring-before(substring-after($text, '{'), '}')"/> <!-- 基于固定上下文计算XPath对应的属性值 --> <xsl:variable name="value" select="$context/$xpath"/> <!-- 递归处理占位符解析后的文本(支持嵌套占位符) --> <xsl:call-template name="expand"> <xsl:with-param name="text" select="$value"/> <xsl:with-param name="context" select="$context"/> </xsl:call-template> <!-- 递归处理剩余未解析的文本 --> <xsl:call-template name="expand"> <xsl:with-param name="text" select="substring-after($text, '}')"/> <xsl:with-param name="context" select="$context"/> </xsl:call-template> </xsl:when> <xsl:otherwise> <xsl:value-of select="$text"/> </xsl:otherwise> </xsl:choose> </xsl:template> </xsl:stylesheet>
关键修正点
- 新增
context参数:调用expand时,把属性所属的元素节点(select="..")传递进去,后续所有XPath计算都基于这个固定节点,不会因递归改变上下文。 - 统一解析占位符:不再区分
{@}、{../@}等不同格式,直接提取大括号内的完整XPath表达式,用$context/$xpath计算对应值,适配任意层级的相对路径。 - 递归保留上下文:每次递归调用
expand都传递$context参数,确保路径计算的基准始终正确。
用这个修正后的XSLT转换,就能得到你预期的结果:<slot>的title属性会正确替换为555+444-333,其他属性的替换也完全符合要求。
内容的提问来源于stack exchange,提问作者eliasum
相关产品推荐
相关产品推荐

