XSLT 3.0中在apply-templates中使用变量进行动态选择?

4
我希望能够将模板应用于一组节点,其中选择路径的一部分是变量。我正在使用Saxon-HE 9.8(非常棒的库!)
我想要实现以下内容:
<variable name="x" select="string('baz')"/>
<xsl:apply-templates select="foo/bar/$x"/>

这似乎不起作用。是否有一种语法可以让我动态构建此apply-templates指令的选择XPath?或者,是否有另一种技术可以动态实现此效果?我甚至尝试将其推到我的<xsl:template match=foo/bar/$x>,但没有成功。

我的动机在于,我的应用程序中变量值来自单独的配置文件。根据配置,我需要运行匹配特定路径段的模板,由配置字符串驱动...


Mads Hansen告诉了你如何做,但没有解释为什么你的代码不起作用。在XSLT / XPath中,变量表示一个值;它不表示XPath表达式的片段。如果$foo的值是字符串“baz”,那么a / b / c / $foo表示a / b / c /“baz”,而不是a / b / c / baz。要评估作为字符串动态提供(在运行时)的任意XPath表达式,您需要xsl:evaluate指令。 - Michael Kay
我相当惊讶的是,这样一个看似非常普遍的用例(动态构造XPath)在XSLT中除了广泛的 xsl:evaluate 指令之外,没有得到解决?另一种选择可能是使用XSLT生成其他XSLT,使用配置文件中的变量值? - shawn
是的,生成XSLT然后执行它是另一种做法。 - Michael Kay
非常感谢你,Michael,感谢你的XSLT智慧! - shawn
2个回答

2

如果变量永远只是简单的字符串值表示元素名称,那么一种选项是更加通用地匹配元素,然后使用字符串变量作为谓词来过滤匹配元素名称:

<xsl:apply-templates select="foo/bar/*[local-name() = $x]"/>

使用Saxon-PE或Saxon-EE,您可以利用xsl:evaluate实现以下功能:

<xsl:variable name="var" as="node()*">
  <xsl:evaluate xpath="concat('foo/bar/',$x)" context-item="."/>
</xsl:variable>
<xsl:apply-templates select="$var"/>

谢谢回复,Mads。但是上面的选择似乎没有匹配到任何内容,即使我直接用字符串值替换$x也是如此。 - shawn
如果将其硬编码为 select="foo/bar/baz",它是否有效?baz 元素是 foo/bar 的子元素,而您的上下文是 foo 元素的父元素?很难诊断为什么它不起作用。您还没有提供 XML 示例或评估此 XSLT 片段的上下文。 - Mads Hansen
1
我的XPath测试工具可能有点老(Webstorm),因为你的解决方案在在线测试工具中可以运行。另外,我的使用情况要求我检查匹配节点的文本值:[*[local-name() = "bodysale"]/text() = "T"] - shawn

2
如果您声明一个静态参数 static parameter <xsl:param name="x" static="yes" as="xs:string" select="'baz'"/> 用于值,然后使用形式为 shadow attribute_select="foo/bar/{$x}" ,甚至可以动态构建路径,但仅在编译XSLT时才能实现。
当然,在静态参数中,您可以引入配置文件并使用其中的值:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    version="3.0">

  <xsl:param name="config-uri" static="yes" as="xs:string" select="'https://martin-honnen.github.io/xslt/2018/config-example1.xml'"/>
  <xsl:param name="config-doc" static="yes" as="document-node()" select="doc($config-uri)"/>

  <xsl:mode on-no-match="shallow-copy"/>

  <xsl:template match="item[@type = 'foo']">
      <xsl:copy>
          <xsl:value-of _select="{$config-doc/map/from[@key = 'foo']}"/>
      </xsl:copy>
  </xsl:template>

  <xsl:template match="item[@type = 'bar']">
      <xsl:copy>
          <xsl:value-of _select="{$config-doc/map/from[@key = 'bar']}"/>
      </xsl:copy>
  </xsl:template>

</xsl:stylesheet>

https://xsltfiddle.liberty-development.net/6qVRKvX/1

在我的第一个答案中没有提到的另一种方法,也是使用Saxon 9.8或任何其他XSLT 3处理器的可行方法是使用XSLT创建XSLT,然后使用transform函数(https://www.w3.org/TR/xpath-functions/#func-transform)运行生成的XSLT。这种方法的优点是它可以与不支持xsl:evaluate的Saxon 9.8 HE一起使用。

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    xmlns:axsl="http://www.w3.org/1999/XSL/Transform-alias"
    exclude-result-prefixes="axsl"
    version="3.0">

  <xsl:param name="config-uri" as="xs:string" select="'https://martin-honnen.github.io/xslt/2018/config-example1.xml'"/>
  <xsl:param name="config-doc" as="document-node()" select="doc($config-uri)"/>

  <xsl:namespace-alias stylesheet-prefix="axsl" result-prefix="xsl"/>

  <xsl:variable name="generated-xslt">
      <axsl:stylesheet version="3.0">
          <axsl:mode on-no-match="shallow-copy"/>
          <xsl:for-each select="$config-doc/map/from">
              <axsl:template match="item[@type = '{@key}']">
                  <axsl:copy>
                      <axsl:value-of select="{.}"/>
                  </axsl:copy>
              </axsl:template>
          </xsl:for-each>
      </axsl:stylesheet>
  </xsl:variable>

  <xsl:mode on-no-match="shallow-copy"/>

  <xsl:template match="/">
      <xsl:sequence 
        select="transform(map {
            'source-node' : .,
            'stylesheet-node' : $generated-xslt
          })?output"/>
  </xsl:template>

</xsl:stylesheet>

https://xsltfiddle.liberty-development.net/6qVRKvX/2


谢谢,但我不认为我可以在我的情况下使用一个“静态”参数,因为它不是全局的,而且这些值是从另一个文件中提取的。 - shawn
虽然静态参数需要是全局的,但您的代码片段并没有展示出需要使用本地参数或变量。至于使用配置文件,您可以在设置“x”的静态参数之前进行操作。我会编辑答案以展示一个例子。 - Martin Honnen

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接