I am working on an XSLT translation that has a single, complicated boolean function appear in multiple loops (and therefore in multiple places within the XSLT document). In order to make the document more robust, specifically to avoid a future edit that updates the boolean test in some of the locations it appears but misses some others, I'd like to have a single declaration of the boolean test that I can call in the various locations. I attempted to do this using an xsl:variable: <xsl:variable name="MatchSelect">not(./Name = preceding::AA_Entry/child::Name) and (./Name = following::AA_Entry/Name)</xsl:variable> then referencing the variable in another location as the test attribute of an xsl:if: <xsl:if test="$MatchSelect"> but what I find is that the xsl:if conditional ends up matching everything (the conditional always evaluates to true). Can anyone help me by telling me this approach definitely won't work, that this should work and I must be making some kind of syntax/dereferencing error (troubleshooting suggestions welcome!), or that there's another approach to realizing this functionality?
Don't forget the scope of your variable: Beginning of the validity of the variable <xsl:variable name="MatchSelect">not(./Name = preceding::AA_Entry/child::Name) and (./Name = following::AA_Entry/Name) ... <xsl:if test="$MatchSelect"> ... </xsl:variable> End of the validity of the variable That should be ok, if not , try to initialize your variable with false!!
Hi Jason! For one thing, your variable should be declared as: <xsl:variable name="MatchSelect" select="not(./Name =preceding::AA_Entry/child::Name) and (./Name = following::AA_Entry/Name)"/> How it is declared now, it simply has a string value of "not(./Name = preceding::AA_Entry..." (try to print its value with <xsl:value-of select="$MatchSelect"/> ) and if a string variable is not empty, <xsl:if test="$varName"> will always evaluate to true. Your XPath expression doesn't "work". Next thing, what you will call from the various locations is the result of once performed test, not the test, applied to your current data. From your post I did not quite understand if it's what you need or not. Regarding scope of variable: if you define your var as a top-level element, (not inside a template) it will be accessible from any place. If you place <xsl:if> inside var declaration, as Guillaume suggested, then the result of <xsl:if> element execution will be added as a part of var value, which may produce an interesting effect, of course. How useful - that's another question
Answering Mapraputa: I see what you mean about why the string always evaluates to 'true' - thanks for that explanation! As for scoping, I am indeed declaring this outside of any templates specifically in order to be able to reference it in multiple templates within the document, which is why the variable-scoping-change suggested by Guillame won't work in my case (I'd have to have multiple templates inside a variable). But, to answer the question you ask, I am intending to use the string as the text of the test, as opposed to intending to have the string evaluated when the variable is declared (creating a true or false) and using the boolean variable later in the document. The solution you propose, in conjunction with where I evaluate the variable (outside of any templates) results in the variable being set to 'false', so that the test condition becomes always-false, which isn't what I'm looking for. Let me try to provide a little more flesh around the problem: <!-- here I declare the variable, creating a string --> <xsl:variable name="UniqueNameTest"> not(./Name=preceding::AA_Entry/child::Name) and (./Name= following::AA_Entry/Name) </xsl:variable> <!-- here is one template I want to use it in for producing html --> <xsl:template match="BigList" mode="HTML"> <xsl:for-each select="BigListEntry"> <!-- as I'm iterating through the list, apply the test to each element of the list --> <xsl:if test="$UniqueNameTest"> An entry matched the unique name test condition. </xsl:if> </xsl:for-each> </xsl:template> <!-- here is another template I want to use it in for producing voicexml --> <xsl:template match="BigList" mode="VoiceXML"> <xsl:for-each select="BigListEntry"> <!-- as I'm iterating through the list, apply the test to each element of the list --> <xsl:if test="$UniqueNameTest"> An entry matched the unique name test condition. </xsl:if> </xsl:for-each> </xsl:template> So, in both cases where the variable appears as: <xsl:if test="$UniqueNameTest"> I want it to be treated as if it was: <xsl:if test="not(./Name=preceding::AA_Entry/child::Name) and (./Name= following::AA_Entry/Name)"> This is a simple framework that I'm hoping shows how I want to use this - define the test condition once (as the string value of a variable) and be able to reference that string to provide the test condition within multiple templates. This would allow me to edit the test condition string once (in the variable declaration) but have the test condition string be used in multiple places throughout the document. I hope this makes it clearer - thanks for the answers so far and any future suggestions!
Leverager of our synergies
Joined: Aug 26, 2000
Yes, now it's perfectly clear! It's perfectly clear that I misunderstood your requirements However, <xsl:if test="$UniqueNameTest"> will not work. Test condition will return true if UniqueNameTest is not empty and that's all. I do not think there is any way to interpret a string as an XPath expression in standard XSLT. Good news is that if you really want this effect, you can use somePrefix:evaluate() extension function, which your XSLT processor may support. Xalan and Saxon provide such function, I do not know about other processors... Another approach is to use a template instead of a variable. <xsl:template name="test"> <xsl:if test=" not(./Name=preceding::AA_Entry/child::Name) and (./Name= following::AA_Entry/Name)">yes</xsl:if> </xsl:template> then you call it as: <xsl:template match="something" > <xsl:variable name="currentTest"> <xsl:call-template name="test"/> </xsl:variable> <xsl:if test="$currentTest='yes'"> some code here </xsl:if> </xsl:template> It doesn't looks too elegant to me , but it's all within standard XSLT, no extensions
Joined: Aug 09, 2001
You are one sly bartender, Mapraputa!! Perhaps not an ideal solution, but I wouldn't be so quick to say it isn't elegant - and most importantly it worked Thank you very much for your help, -Jason