Setting a variable conditionally with no cut of ancestor axis
Asked Answered
S

1

1

I have xslt/xpath v1.0 stack under .NET.

I want to set a variable $myVar conditionally:

 <xsl:variable name="myVar">
   <xsl:if test="$case ='1'">
     <xsl:copy-of select="$otherVarA/down/the/rabbit/hole"/>
   </xsl:if>
  <xsl:if test="$case ='2'">
     <xsl:copy-of select="$otherVarB/down/the/rabbit/hole"/>
   </xsl:if>
   <xsl:if test="$case ='3'">
     <xsl:copy-of select="$otherVarC/down/the/rabbit/hole"/>
   </xsl:if>        
 </xsl:variable>

Later on there are downward accesses: $myVar/go/deeper but also upward accesses like so $myVar/ancestor::rabbit.

Obviously <xsl:copy-of select="$myVar/down/to/rabbit/hole"/> cuts the path upwards.

How can i set $myVar way in order to access the ancestor axis?

I know that <xsl:variable name=... select=... does not cut upward axis.

Spleenwort answered 5/9, 2019 at 9:17 Comment(0)
M
2

The first problem you have got, is that in XSLT 1.0 when you use xsl:copy-of within a variable, the variable itself will be of type "Result Tree Fragment", and so you won't be able to use xpath expressions like $myVar/down/to/rabbit/hole on it. You would need to use the "node-set" extension function to convert it from a Result Tree Fragment to a node-set which you could access the nodes in (See https://www.xml.com/pub/a/2003/07/16/nodeset.html for more details).

However, this won't solve you main problem, about getting the ancestor. Because you are using xsl:copy-of, you will only be able to access the nodes you have copied, not any of their ancestors in the original XML that weren't copied across.

I suspect your real case may be more complicated, but given what you have shown in your question, you could define the variable like so:

 <xsl:variable name="myVar"
               select="$otherVarA[$case ='1']/down/the/rabbit/hole
                       |$otherVarB[$case ='2']/down/the/rabbit/hole
                       |$otherVarC[$case ='3']/down/the/rabbit/hole"/>

This way, you are now referencing the nodes in the original XML, rather than making copies, and so you won't need the node-set function, and the ancestor function can access all ancestors in the original XML.

This does assume your $otherVarA/B/C variables are references to the original XML, and not result-tree-fragments created with xsl:copy-of too.

See http://xsltfiddle.liberty-development.net/bwdwrw for a contrived example.

Massey answered 5/9, 2019 at 10:46 Comment(5)
Thank you. I know about "node-set". But it may help others. Will the $otherVarABC vars will be evaluated, even if the corresponding $case condition is false?Spleenwort
The XSLT/XPath specification very rarely (especially in 1.0) says whether something is evaluated or not, when evaluation isn't logically necessary. Optimizations are entirely up to the implementation, and may vary considerably from one implementation to another.Jasso
In this case (.Net and XSLT/XPath v1.0) all vars will be evaluated. :( I used this code (codepad.org/Ln7w1TTe ) for testing. (See output at the end.)Spleenwort
If you are concerned about performance, and want to avoid unnecessary evaluation, you could make use of named templates with parameters instead. See xsltfiddle.liberty-development.net/bwdwrw/1 for another contrived example.Massey
Thanks. I guess with this named template solution i need to know the actual value of $case / $c where the ancestor access is done? If yes, then your solution is similar to my current approach (not shown here).Spleenwort

© 2022 - 2024 — McMap. All rights reserved.