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

时间:2018-05-08 00:26:06

标签: xslt

我想将模板应用于一组节点,其中select路径的一部分是变量。我使用Saxon-HE 9.8(很棒的lib!)

我试图实现以下目标

<variable name="x" select="string('baz')"/>
<xsl:apply-templates select="foo/bar/$x"/>

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

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

2 个答案:

答案 0 :(得分:2)

如果您为该值声明static parameter <xsl:param name="x" static="yes" as="xs:string" select="'baz'"/>,然后以_select="foo/bar/{$x}"的形式使用shadow attribute,您甚至可以动态构建路径,但仅在编译时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

答案 1 :(得分:1)

如果你的变量总是一个表示元素名称的简单字符串值,那么一个选项就是在元素上更一般地匹配,然后在谓词中使用字符串变量来过滤匹配元素名称:

<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"/>