When using XSLT how do you test to see if a locally scoped variable exists, or is this even possible?
Considering the XSLT stylesheet as an XML DOM, a variable declaration element makes the variable visible to all following siblings and their descendants. This allows XSLT processors to statically analyze any XPath containing a variable reference to see if the variable exists; if the variable declaration exists on the preceding-sibling or ancestor axis, the variable reference is legal, otherwise it's not.
Note that this is entirely dependent on the structure of the XSLT, not the structure of the XML it's processing. The XSLT processor can and should produce an error if an XPath expression uses a variable that doesn't exist.
There's no way to check for this condition inside XSLT because this condition isn't legal within XSLT. The sitauation you described in your comment - "The idea is to set a flag variable if something is output and later on display a different message if nothing was output." - actually should result in a syntax error. For instance, if you do something like this:
<xsl:if test="some_condition">
<!-- produce output here -->
<xsl:variable name="flag">true</xsl:variable>
</xsl:if>
<!-- time passes -->
<xsl:if test="$flag='true'>
<!-- wouldn't it be nice? -->
</xsl:if>
you'll get a syntax error: the second xsl:if element is neither a following sibling of the variable declaration nor one of their descendants.
Here's a technique I use a fair amount - this produces variable output based on a variety of different conditions that you don't want to re-check later:
<xsl:variable name="output">
<xsl:if test="$condition1='true'">
<p>condition1 is true</p>
</xsl:if>
<xsl:if test="$condition2='true'">
<p>condition2 is true</p>
</xsl:if>
<xsl:if test="$condition3='true'">
<p>condition3 is true</p>
</xsl:if>
</xsl:variable>
<!-- we've produced the output, now let's actually *output* the output -->
<xsl:copy-of select="$output"/>
<!-- time passes -->
<xsl:if test="normalize-space($output) != ''">
<p>This only gets emitted if $output got set to some non-empty value.</p>
</xsl:if>
Asking this question indicates that you did not fully grasp the key point of XSLT. :-)
It's declarative: nothing can exist unless you declare it. You declare a variable, then it's there, you don't, then it's not.
Not once will there be the point where you have to wonder, while coding, if a certain variable exists.
XSLT has strict scoping rules, variables exist only within the scope of their parent element, (and not all elements can contain variables to begin with). Once you leave the parent element, the variable is gone.
So unless you specify your question/intent some more, the only valid answer is that the question is wrong. You cannot and do not need to check if a variable exists at run-time.
XSL variables are strictly scoped, so you can't access them in sibling nodes, only in children.
If you are dealing with params, you can use a global <xsl:param />.
See: http://www.stylusstudio.com/xsllist/199911/post30020.html
Best and fast idea to check value if it's exist is to check it length
<xsl:if test="string-length(value/to/check)=0">
</xsl:if>
I don't think it's possible, but you're not likely to ever need it, because the variable doesn't exist unless you've declared it.
If you have a variable, you can check it has something, or it "exists" by doing something like the following:
<xsl:choose>
<xsl:when test="$myvar">
This variable exists!
</xsl:when>
<xsl:otherwise>
The variable doesn't exist :(
</xsl:otherwise>
</xsl:choose>
As for its validity I cannot be certain. I will tell you however, that I do this in some of our systems at work ;)
Related
The problem is I want to present two numbers that are stored as strings nicely in a file I generate.
The following code works just fine, but the formatting is not really the one I would like it to be:
<xsl:call-template name="create-cell">
<xsl:with-param name="value" select="concat($value_one,' or ',$value_two)" />
So I thought that I'll add a format-number function and give the numbers the format I want them to have. In order to do so I cast the string to number, use the function and then cast back to string, like this:
<xsl:call-template name="create-cell">
<xsl:with-param name="value" select="concat(string(format-number(number($value_one), "###,###.00"),' or ',$value_two)" />
But the transformation ends without results (an error occurs). What exactly am I doing wrong when changing the type?
EDIT:
The error that occurs is:
Element type "xsl:with-param" must be followed by either attribute specifications, ">" or "/>"
I guess the solution is to escape the double quotas with " ...
This part :
<xsl:with-param name="value" select="concat(string(format-number(number($value_one), "###,###.00"),' or ',$value_two)" />
should be written as:
<xsl:with-param name="value" select="concat(format-number($value_one, '#,###.00'), ' or ', $value_two)"/>
Whether that's a good approach to solve the actual problem (of which we know practically nothing) is another question.
But the transformation ends with result -
My guess is that $value_one is not a number. Perhaps it is formatted as 12,345.00? If the conversion to an xs:double fails, it will return NaN, which is a valid number, but not really. Using the format-number function, it should return NaN, regardless the picture string.
If your variable holds the empty sequence, format-number will return the empty sequence.
Since format-number already returns a string, using string() on the result will have no effect.
If the - in your question meant a literal minus-sign, then perhaps update your question with what your first snippet and second snippet return exactly, and what processor (exact version) you use.
Update (after you posted the error)
Your XSLT is not valid XML. All XSLT must be valid XML to begin with. Michael's answer shows where it goes wrong: an attribute value must be enclosed in quotes, and yours wasn't.
I'm trying to set a variable with a dynamic name. This means the name for my new variable comes from another variable:
<#-- in real world I wouldn't declare this variables right here -
they would come from somewhere else -->
<#assign varName = "myVarName"/>
<#assign varValue = "myVarValue/>
<#... set the variable .../>
So that the value can be referenced as follows:
${myVarName} <#-- prints "myVarValue" -->
In a Java directive I would use
Environment#setVariable(String name, TemplateModel model)
to achieve this. But is there a possibility to achieve this with Freemarker directly?
I had a similar problem and Special Variable Reference page helped me:
vars: Expression .vars.foo returns the same variable as expression foo. It's useful if for some reasons you have to use square bracket syntax, since that works only for hash sub variables, so you need an artificial parent hash. For example, to read a top-level variable that has a strange name that would confuse FreeMarker, you can write .vars["A strange name!"]. Or, to access a top-level variable with dynamic name given with variable varName you can write .vars[varName]. Note that the hash returned by .vars does not support ?keys and ?values.
In my case I had to use only strings in my model. I had a bunch of names like Name1, Name2, ... Name10. To make a table of these names I used this code:
<#list 1..10 as x>
<#if .vars["Name" + x]??>
<tr>
<td align="center">${.vars["Name" + x]}</td>
</tr>
</#if>
</#list>
Use a hash. That is, use the name of the variable as the key of the hash.
There's no directive that assigns to a variable that has a dynamic name. But here's a hack to achieve that:
<#'<#assign ${varName} = varValue>'?interpret />
This isn't terribly fast though. It involves FTL parsing each time it's evaluated.
I guess you can do it like this:
${myVarName?eval} <#-- prints "myVarValue" -->
I found the answer from here, and it works to me.
Please consider my "A/B" xPath expression returns the following node
<Q ID="12345">
----
----
</Q>
This is my variable
This is how I’m trying to assign a value to my tempVariable variable
<xsl:for-each select="A/B">
<xsl:variable name="tempVariable"><xsl:value-of select="#ID"/></xsl:variable>
</xsl:for-each>
And after all I’m trying to use this variable
<xsl:if test="$tempVariable='12345'">
....
....
</xsl:if>
but here as I understand I’m getting $tempVariable ="" which is not correct.
Can someone please tell me where I’m doing wrong or how can I do this in proper way.
Thank you.
Why would a path like A/B select a Q element? If you want to use a variable you need to make sure it is in scope. The variable you show in your sample is in scope inside the xsl:for-each, after the xsl:variable element.
If you want to use the variable outside the for-each you would need to declare it outside the for-each.
However I think you can simply do
<xsl:variable name="v1" select="A/B/#ID"/>
<xsl:if test="$v1 = '12345'">..</xsl:if>
there is no need for the for-each.
So this is fairly simple I think, it could be more involved then I understand though.
Anyways, at the moment I'll just include the code I believe to be relevant, if someone thinks that more would be relevant then I will try to include more.
I've got two statements:
<xsl:value-of select="$isOnlineColumnEmpty" />
<xsl:value-of select="not($isOnlineColumnEmpty = 'false')" />
The first one outputs false. However, the second one is outputting true, which is the opposite of what I expect.
In addition to this, These two statements get repeated multiple times, and I only get this result in one section. However, there is absolutely no code in between these two statements, so nothing could be changing the value of the variable.
Just wondering if there is something I could be overlooking?
Instead of:
<xsl:value-of select="not($isOnlineColumnEmpty = 'false')" />
use:
<xsl:value-of select="not($isOnlineColumnEmpty = false())" />
Explanation:
The boolean value false() is something different from the string "false".
When a boolean is compared to another value, the second value is converted to a boolean and then the comparisson is performed. To quote the XPath 1.0 W3C Specification:
"If at least one object to be compared is a boolean, then each object
to be compared is converted to a boolean as if by applying the boolean
function."
So, in this concrete case the string "false" is converted to boolean. By definition boolean($someString) is true() for any non-empty string.
Therefore, after the conversion the two boolean values false() and true() are compared and the result is false(). Because in the original expression this result is argument to the not() function, the final result is not(false()) , that is true().
Here is the chain of calculations:
not($isOnlineColumnEmpty = 'false') ==> not(false() = 'false') ==>
not(false() = boolean(false())) ==> not(false() = true()) ==>
not(false()) ==> true()
So, I figured this out later.
Changing the statement to:
<xsl:value-of select="$isOnlineColumnEmpty != false" />
Fixed the issue for me.
first time SO user :)
I know that I can format a number like this:
format-number($value, '###,###.00')
But I would like to remove the dot and the zeroes if $value is zero.
So,
37368 -> 37,368
2.666667 -> 2.66
Is this possible using just number formatting (doesn't seem like it) or do I have to do something along the lines of
if (int(value) == value ) {...}
format-number($value, '###,###.##')
xpath 2.0 contains conditional logic if..then..else but in the more likely case that you're in xpath 1.0 I'm afraid you'll have to either rely on XSLT to do this for you, i.e:
<xsl:variable name="myvar">
<xsl:choose>
<xsl:when test="$value > 0">
<xsl:select="format-number($value, '###,###.00')"/>
</xsl:when>
<xsl:otherwise>0</xsl:otherwise>
</xsl:choose>>
</xsl:variable>
..or mixin an extension (.NET can do this natively, EXSLT exists otherwise, probably more options available)
To the best of my knowledge no xpath functions exist to get you out of this, but I could be missing something exotic.