如何在XSLT 2.0/3.0中实现动态函数调用(高阶函数)?
XSLT 2.0/3.0中实现动态函数调用(高阶函数)的方法
问题场景
以下XSLT 3.0代码尝试实现动态函数调用,但在Saxon中运行失败:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:kooks="kookerella.com" xmlns:xs="http://www.w3.org/2001/XMLSchema" exclude-result-prefixes="xs" version="3.0"> <xsl:function name="kooks:one" as="xs:integer"> <xsl:sequence select="1"/> </xsl:function> <xsl:function name="kooks:two" as="xs:integer"> <xsl:sequence select="2"/> </xsl:function> <xsl:template match="/"> <xsl:variable name="two" select="kooks:two()"/> <xsl:variable name="two_func" select="kooks:two"/> <xsl:variable name="two_2" select="$two_func()"/> <root> <xsl:sequence select="$two_2"/> </root> </xsl:template> </xsl:stylesheet>
报错信息:
唯一能通过类型检查的值是空序列。
动态函数调用目标的所需项类型为function(*),但提供的表达式{$two_func}的项类型为element(Q{kookerella.com}two)
核心原因:kooks:two在XPath语法中会被解析为选择命名空间kookerella.com下的<two>元素,而非引用同名函数,因此无法作为函数对象调用。
解决方案
1. XSLT 3.0(原生支持高阶函数)
XSLT 3.0基于XPath 3.0+,原生支持函数引用,可通过两种方式实现动态调用:
方法一:直接引用函数对象
使用function()构造器获取函数引用,适用于明确知道函数名的场景:
<xsl:template match="/"> <xsl:variable name="two_func" select="function(kooks:two)"/> <xsl:variable name="two_2" select="$two_func()"/> <root> <xsl:sequence select="$two_2"/> </root> </xsl:template>
方法二:动态根据QName获取函数
如果需要通过字符串动态指定函数名,可结合QName()和function-lookup()实现:
<xsl:template match="/"> <!-- 动态定义要调用的函数名 --> <xsl:variable name="target-func" select="QName('kookerella.com', 'two')"/> <!-- 第二个参数为函数的参数个数,这里kooks:two是无参函数,传入0 --> <xsl:variable name="two_func" select="function-lookup($target-func, 0)"/> <xsl:if test="$two_func"> <root> <xsl:sequence select="$two_func()"/> </root> </xsl:if> </xsl:template>
2. XSLT 2.0(无原生高阶函数,间接模拟)
XSLT 2.0不支持原生高阶函数,可通过以下两种方式模拟动态调用:
方法一:分支选择函数调用
通过<xsl:choose>根据标识选择对应函数,适合有限分支的场景:
<xsl:template match="/"> <!-- 动态标识要调用的函数 --> <xsl:variable name="func-key" select="'two'"/> <xsl:variable name="result"> <xsl:choose> <xsl:when test="$func-key = 'one'"> <xsl:sequence select="kooks:one()"/> </xsl:when> <xsl:when test="$func-key = 'two'"> <xsl:sequence select="kooks:two()"/> </xsl:when> </xsl:choose> </xsl:variable> <root> <xsl:sequence select="$result"/> </root> </xsl:template>
方法二:动态调用模板
将逻辑封装到命名模板中,通过动态指定模板名实现类似效果:
<xsl:template name="kooks:one-tmpl"> <xsl:sequence select="1"/> </xsl:template> <xsl:template name="kooks:two-tmpl"> <xsl:sequence select="2"/> </xsl:template> <xsl:template match="/"> <xsl:variable name="target-tmpl" select="'kooks:two-tmpl'"/> <root> <xsl:call-template name="{$target-tmpl}"/> </root> </xsl:template>
内容的提问来源于stack exchange,提问作者MrD at KookerellaLtd
相关产品推荐
相关产品推荐

