在XSLT中实现动态XPath:saxon:evaluate简化for-each-group遇阻
解决Saxon:evaluate简化XSLT重复代码的问题
刚接触XSLT就想着用动态求值来减少重复代码,这个思路很棒!不过你的尝试里有几个关键问题导致报错,咱们一步步拆解修复:
你当前代码的核心问题
- 错误地将节点值当成XPath表达式传递:你用
<xsl:value-of>把匹配到的节点内容提取成了字符串,但saxon:evaluate需要的是XPath表达式字符串,不是节点的文本内容。比如$oSel里的$compose//*[contains(@outputclass,$sel)]应该是作为字符串传递,而不是直接计算节点后取文本。 - 分组表达式的上下文错误:
group-by里的表达式是针对每个分组节点的,你之前的$oGroup构造没有考虑到这个上下文,导致求值时找不到正确的节点。
修正后的实现方案
首先确保你的XSLT开头声明了Saxon命名空间:
xmlns:saxon="http://saxon.sf.net/"
然后重新构造XPath表达式字符串,而不是直接计算节点:
<!-- 构造选择节点的XPath表达式字符串 --> <xsl:variable name="selectExpr"> <xsl:choose> <xsl:when test="starts-with($from,'oclass-')"> <xsl:text>$compose//*[contains(@outputclass, '</xsl:text> <!-- 转义单引号避免X语法错误 --> <xsl:value-of select="replace($sel, ''', '''')"/> <xsl:text>')]</xsl:text> </xsl:when> <xsl:when test="starts-with($from,'node-')"> <xsl:text>$compose//*[name()='</xsl:text> <xsl:value-of select="replace($sel, ''', '''')"/> <xsl:text>']</xsl:text> </xsl:when> <xsl:otherwise>()</xsl:otherwise> </xsl:choose> </xsl:variable> <!-- 构造分组依据的XPath表达式字符串 --> <xsl:variable name="groupExpr"> <xsl:choose> <xsl:when test="ends-with($from,'-attribute')"> <xsl:text>@*[local-name()='</xsl:text> <xsl:value-of select="replace($group, ''', '''')"/> <xsl:text>']</xsl:text> </xsl:when> <xsl:when test="ends-with($from,'-childnode')"> <xsl:text>*[name()='</xsl:text> <xsl:value-of select="replace($group, ''', '''')"/> <xsl:text>']</xsl:text> </xsl:when> <xsl:when test="ends-with($from,'-parentnode')"> <xsl:text>parent::*[name()='</xsl:text> <xsl:value-of select="replace($group, ''', '''')"/> <xsl:text>']</xsl:text> </xsl:when> <xsl:when test="ends-with($from,'-followingnode')"> <xsl:text>following-sibling::*[name()='</xsl:text> <xsl:value-of select="replace($group, ''', '''')"/> <xsl:text>'][1]</xsl:text> </xsl:when> <xsl:when test="ends-with($from,'-precedingnode')"> <xsl:text>preceding-sibling::*[name()='</xsl:text> <xsl:value-of select="replace($group, ''', '''')"/> <xsl:text>'][1]</xsl:text> </xsl:when> <xsl:otherwise>()</xsl:otherwise> </xsl:choose> </xsl:variable> <!-- 使用saxon:evaluate执行动态分组 --> <xsl:for-each-group select="saxon:evaluate($selectExpr)" group-by="saxon:evaluate($groupExpr)"> <xsl:sort select="current-grouping-key()"/> <element key="{current-grouping-key()}"> <xsl:if test="number(current-group()[1]) = number(current-group()[1])"> <xsl:attribute name="max" select="max(current-group())"/> <xsl:attribute name="sum" select="sum(current-group())"/> </xsl:if> <xsl:copy-of select="current-group()[1]"/> </element> </xsl:for-each-group>
额外注意事项
- 单引号转义:用
replace($sel, ''', '''')处理参数中的单引号,避免XPath表达式语法错误。 - Saxon版本兼容性:你用的9.1.0.8支持
saxon:evaluate,但要确保变量$compose在当前上下文可见(如果是全局变量就没问题)。 - 空值处理:添加
<xsl:otherwise>()</xsl:otherwise>避免无效表达式导致的报错。
这样就能实现你想要的动态选择和分组逻辑,大幅减少重复代码啦!
内容的提问来源于stack exchange,提问作者Aaron




