7

I'm stuck with a small problem.

The XSL-File:

<?xml version="1.0" encoding="UTF-8"?> 
<xsl:stylesheet version="1.0"  
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:fn="http://www.w3.org/2005/xpath-functions"> 
<xsl:template match="/"> 


<xsl:variable name="unumericValue" select="10" />
<xsl:variable name="uanotherValue" select="8" />



<xsl:for-each select="/root/try">
<xsl:value-of select="var" />
<xsl:variable name="min"><xsl:value-of select="@minimum" /></xsl:variable>
<xsl:value-of select="@type" />
<xsl:variable name="referenceName"><xsl:value-of select='concat("u",var)' /></xsl:variable>
<xsl:value-of select="$referenceName" />
<xsl:if test='$referenceName > $min'>
<p>Do something.</p>
</xsl:if>
</xsl:for-each>

</xsl:template>
</xsl:stylesheet>

The XML File:

<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" href="q1.xsl"?>
<root>
<try type="compare" minimum="9">
<var>numericValue</var>
<something>...</something>
</try>

<try type="compare" minimum="10">
<var>anotherValue</var>
<something>...</something>
</try>
</root>

As you can see the XML file has two var-Elements which should match to the variables in the XSLT-File. However I don't know which Syntax is correct. $referenceName is just the name of the variable which I want to use. But I don't know how to reference the name to the existing variable.

Peter O.
  • 32,158
  • 14
  • 82
  • 96
eadrax
  • 73
  • 1
  • 1
  • 3
  • Does this answer your question? [Indirect variable/parameter reference (name in another property / another variable)](https://stackoverflow.com/questions/13207477/indirect-variable-parameter-reference-name-in-another-property-another-variab) – GSerg Mar 25 '21 at 09:34

3 Answers3

11

$referenceName isn't a reference to the variable with the name "unumericValue" or otherwise. It's just the string value "unumericValue", etc. So that will never be greater than $min. However, with a little extra work, there is a trick to find a variable by its name:

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

  <xsl:variable name="numericValue" select="10" />
  <xsl:variable name="anotherValue" select="8" />
  <xsl:variable name="vars" select="document('')/*/xsl:variable" />

  <xsl:template match="/">
    <xsl:variable name="referenceName" select="'numericValue'" />
    <xsl:variable name="referenceValue" select="$vars[@name = $referenceName]/@select" />
    Reference value: <xsl:value-of select="$referenceValue" />
  </xsl:template>
</xsl:stylesheet>

One big limitation to note here is that this will only work for variables that are a constant numeric value.

Here's a way to simulate variables with constant string values:

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                 xmlns:v="variables-node"
>
  <v:variables>
    <v:variable n="numericValue" value="10" />
    <v:variable n="nonNumericValue" value="Hello World" />
  </v:variables>
  <xsl:variable name="vars" select="document('')//v:variables/v:variable" />

    <xsl:template match="/">
      <xsl:variable name="referenceName" select="'nonNumericValue'" />
      <xsl:variable name="referenceValue" select="$vars[@n = $referenceName]/@value" />
      <xsl:value-of select="concat('The variable with the name ', $referenceName, ' has the value ', $referenceValue)"/>
    </xsl:template>
</xsl:stylesheet>

And lastly, a way to simulate variables with calculated values:

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" 
                xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                xmlns:exslt="http://exslt.org/common"
>

  <xsl:variable name="varsRaw">
    <var n="computedValue" value="{concat('2 + 4 is ', 2 + 4)}" />
    <var n="computedNumber" value="{22 div 7}" />
  </xsl:variable>
  <xsl:variable name="vars" select="exslt:node-set($varsRaw)/var" />

    <xsl:template match="/">
      <xsl:variable name="referenceName" select="'computedValue'" />
      <xsl:variable name="referenceValue" select="$vars[@n = $referenceName]/@value" />
      <xsl:value-of select="concat('The variable with the name ', $referenceName, ' has the value ', $referenceValue)"/>

      <xsl:value-of select="'     '"/>

      <xsl:variable name="referenceName2" select="'computedNumber'" />
      <xsl:variable name="referenceValue2" select="$vars[@n = $referenceName2]/@value" />
      <xsl:value-of select="concat('The variable with the name ', $referenceName2, ' has the value ', $referenceValue2)"/>
    </xsl:template>
</xsl:stylesheet>

The last approach is probably actually the most orthodox, but requires the XSLT processor-dependent (at least in XSLT 1.0) node-set() function.

JLRishe
  • 99,490
  • 19
  • 131
  • 169
6

By the way, don't do this:

<xsl:variable name="min"><xsl:value-of select="@minimum" /></xsl:variable>

when you could do this:

<xsl:variable name="min" select="@minimum" />

It's not only verbose, it's also inefficient - there's no need to copy the data and construct a new tree, which is a very expensive operation, when all you want is a reference to an existing node.

Michael Kay
  • 156,231
  • 11
  • 92
  • 164
1

As with most programming languages, XSLT variable names aren't accessible at run-time. The variable might not even exist at run-time - the optimizer is allowed to play all sorts of tricks, like inlining all references to the variable at the point where the variable is used.

The best approach is to have a variable with a standard name, and give it XML content. The element and attribute names in the XML are accessible at run-time, unlike the variable names.

Michael Kay
  • 156,231
  • 11
  • 92
  • 164