XSLT 3.0 Dynamic select with variable in apply-templates?

浪子不回头ぞ 提交于 2021-01-27 12:09:02

问题


I want to apply templates to a set of nodes where part of the select path is a variable. I'm using Saxon-HE 9.8 (awesome lib!)

I'm trying to achieve the following

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

This doesn't seem to work. Is there a syntax that will allow me to dynamically construct the select XPath for this apply-templates instruction? Or, is there another technique for dynamically achieving this effect? I even tried pushing this down to my <xsl:template match=foo/bar/$x> but no luck.

My motivation here is in my application the variable value is coming from a separate configuration file. Based on the configuration I need to run templates matching specific path segments driven by config strings...


回答1:


If your variables are always going to be a simple string value expressing the name of an element, then one option would be to match a little more generically on an element and then use the string variable in a predicate to filter for a match of the element name:

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

With Saxon-PE or Saxon-EE, you could leverage xsl:evaluate and do something like this:

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



回答2:


If you declare a static parameter <xsl:param name="x" static="yes" as="xs:string" select="'baz'"/> for the value and then use a shadow attribute in the form of _select="foo/bar/{$x}" you can even construct the path dynamically, but only when compiling the XSLT.

In a static parameter you can of course pull in a configuration file and use values from it:

<?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

Another option I didn't mention in my first answer but that is also a viable way with Saxon 9.8 or any other XSLT 3 processor is the use of XSLT to create XSLT and then to use the transform function (https://www.w3.org/TR/xpath-functions/#func-transform) to run that generated XSLT. That approach has the advantage that it works with Saxon 9.8 HE where xsl:evaluate is not supported:

<?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



来源:https://stackoverflow.com/questions/50224145/xslt-3-0-dynamic-select-with-variable-in-apply-templates

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!