XSLT:Use of concat function - xslt

I'm trying to display only limited characters in html td tag,following is my code,
if "VAR1" has less than 10 character,it's displaying '...' directly,how to check this out???
<td><xsl:value-of select="concat(substring(VAR1,1,10),'...')"/></td>

If you want to only show 10 characters, but only show the ... when there are more than 10 characters in the original string, you could do this in a single expression live so
<xsl:value-of select="concat(
substring(VAR1,1,10),
substring('...', 1 div (string-length(VAR1) > 10)))"/>
So, when VAR1 is '123456789' it will output just this
123456789
But when VAR1 is '123456789012' it will output this
1234567890...
To explain how this works, the following expression will either be true or false, depending on whether the length of the string is more than 10 or not
(string-length(VAR1) > 10)
When used in a numeric expression, true evaluates to 1, and false evaluates to 0. Now, in the case of the string being more than 10 characters in length, the full expression is evaluated like so
substring('...', 1 div (string-length(VAR1) > 10)))
= substring('...', 1 div true)
= substring('...', 1 div 1)
= substring('...', 1)
= '...'
So, with more than 10 characters, you get the '...' at the end.
However, when you have less than 10 characters, it evaluates like so
substring('...', 1 div (string-length(VAR1) > 10)))
= substring('...', 1 div false)
= substring('...', 1 div 0)
= substring('...', (A very big number!))
= ''
So, with less than 10 characters, the substring does not return anything.

if "VAR1" has less than 10 character,it should display '...' ? if this is your question, the following is the answer
<td>
<xsl:choose>
<xsl:when test='string-length(VAR1) > 10'>
<xsl:value-of select=concat(substring(VAR1,1,10),'...')/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="VAR1"/>
</xsl:otherwise>
</xsl:choose>
</td>

Related

How to check if variable is Null or empty in XSLT?

I definded the following variables:
<xsl:variable name="pica036E"
select="recordData/record/datafield[#tag='036E']" />
<xsl:variable name="pica036F"
select="recordData/record/datafield[#tag='036F']" />
Now I need to do a condition if variable pica036E isn't empty and pica036F is empty show the following message otherwise show another message.
That's my code, but I don't ge any output. Is "null or empty" correct defined?
<xsl:choose>
<xsl:when test="$pica036E != '' and $pica036F = ''">
<xsl:message>
036F no 036E yes
</xsl:message>
</xsl:when>
<xsl:otherwise>
<xsl:message>
036E no 036F yes
</xsl:message>
</xsl:otherwise>
</xsl:choose>
In XPath, X=Y means (if some pair x in X, y in Y satisfy x = y), while X != Y means (if some pair x in X, y in Y satisfy x != y).
This means that if either X or Y is an empty sequence, then both X=Y and X!=Y are false.
For example, $pica036E != '' tests whether there is a value in $pica036E that is not a zero-length string. If there are no values in $pica036E then there is no value that satisfies this condition.
As a result, using != in XPath is always a code smell. Usually, rather than X != Y, you should be writing not(X = Y).
Check following Code. I think your output get
<xsl:when test="not($pica036E = '') and $pica036F = ''">
In XSLT a variable with text content can also serve as a boolean variable.
Not empty content means true, empty content means false.
So the condition can be also written as:
<xsl:when test="$pica036E and not($pica036F)">
Remember that not is a function (not an operator).

Weird behavior of the != XPath operator

I'm attempting to create an xsl:choose statement with multiple conditions to test. So far, I have this:
<xsl:choose>
<xsl:when test="$AccountNumber != '12345' and $Balance != '0'">
<do stuff here>
...
The problem is that the 'and' is being treated as an 'or'. If the account number is 12345 or the balance of an account is 0, the condition is treated as true and the code gets executed. I need the test to be that both conditions must be true... do I have the syntax wrong here?
Thanks in advance,
~Tim
The problem is that the 'and' is being treated as an 'or'.
No, the problem is that you are using the XPath != operator and you aren't aware of its "weird" semantics.
Solution:
Just replace the any x != y expressions with a not(x = y) expression.
In your specific case:
Replace:
<xsl:when test="$AccountNumber != '12345' and $Balance != '0'">
with:
<xsl:when test="not($AccountNumber = '12345') and not($Balance = '0')">
Explanation:
By definition whenever one of the operands of the != operator is a nodeset, then the result of evaluating this operator is true if there is a node in the node-set, whose value isn't equal to the other operand.
So:
$someNodeSet != $someValue
generally doesn't produce the same result as:
not($someNodeSet = $someValue)
The latter (by definition) is true exactly when there isn't a node in $someNodeSet whose string value is equal to $someValue.
Lesson to learn:
Never use the != operator, unless you are absolutely sure you know what you are doing.
If $AccountNumber or $Balance is a node-set, then this behavior could easily happen. It's not because and is being treated as or.
For example, if $AccountNumber referred to nodes with the values 12345 and 66 and $Balance referred to nodes with the values 55 and 0, then
$AccountNumber != '12345' would be true (because 66 is not equal to 12345) and $Balance != '0' would be true (because 55 is not equal to 0).
I'd suggest trying this instead:
<xsl:when test="not($AccountNumber = '12345' or $Balance = '0')">
$AccountNumber = '12345' or $Balance = '0' will be true any time there is an $AccountNumber with the value 12345 or there is a $Balance with the value 0, and if you apply not() to that, you will get a false result.
I've always used this syntax, which yields more predictable results than using !=.
<xsl:when test="not($AccountNumber = '12345') and not($Balance = '0')" />

Boolean "sum" in XSLT

I know I can sum over multiple nodes with numeric values.
How could I do a "boolean sum" over a set of nodes? For example:
<a>
<b>false</b>
<b>false</b>
<b>true</b>
<b>false</b>
</a>
How could I get the boolean OR of all the <b> node values? (which should be 'true').
Use:
boolean(/*/b[. = 'true'])
This produces the boolean value of the expression:
/*/b[. = 'true']
and is true exactly when the above expression selects at least one node -- that is, when there is a b that is a child of the top element and whose string value is the string 'true.
In case you want also to calculate the "boolean product" (using and), do:
not(/*/b[. = 'false'])
You could count the true values:
<xsl:if test="count(a/b[text()='true']) > 0">
true
</xsl:if>
If there are any true values the OR will be true.

XSLT 1.0 Idiom for ternary if?

This Java program makes use of a Ternary if, to map booleans to output strings: (a "*" for true, an empty string for false).
public class ternary {
public static void main(String[] args) {
boolean flags[]={true,false,true};
for (boolean f : flags) {
System.out.println(f?"*":"");
}
}
}
So the output is *, [empty], *.
I have an input XML document, something like:
<?xml version="1.0" ?>
<?xml-stylesheet type="text/xsl" href="change.xsl"?>
<root>
<change flag="true"/>
<change flag="false"/>
<change flag="true"/>
</root>
And I have the following XSLT template which maps true to '*' and false to '' (it works):
<xsl:template match="change">
<xsl:variable name="flag" select="translate(substring(#flag,1,1),'tf','*')"/>
<xsl:value-of select="$flag"/>
</xsl:template>
Is there is more concise version of this ?
a) Can I automatically get a value of boolean true|false directly from the string 'true|false' ?
b) Is there an (xpath?) construct to map the boolean true|false to '*', '' ?
a) Can I automatically get a value of boolean true|false directly from
the string 'true|false' ?
b) Is there an (xpath?) construct to map the boolean true|false to
'*', '' ?
This is actually an XPath question.
I. XPath 1.0
There are more than one XPath expressions the evaluation of which produces the wanted result:
substring('*', 2 -(#flag = 'true'))
This also answers b) -- notice that the strings 'true' and 'false' aren't boolean values! The only two boolean values are true() and false() and they are not strings.
In the above expression we use the fact that in XPath 1.0 if a boolean is in a context where a number is needed, it is automatically converted to number. By definition:
number(true()) is 1
and
number(false()) is 0
Thus the second argument of the call to substring() above:
2 - (#flag = 'true')
is evaluated as 1 when #flag = 'true' and to 2 otherwise.
A more general XPath 1.0 expression that produces the string $s1 if $val is "x" and produces the string $s2 if $val is "y" :
concat(substring($s1, 1 div ($val = "x")),
substring($s2, 1 div ($val = "y"))
)
This produces the string $s1 when $val = "x", the string $s2 when $val = "y", and the empty string -- if none of these two conditions holds.
The above XPath 1.0 expression can be generalized to produce N different string results $s1, $2, ..., $sN exactly when $val is one of the values $v1, $v2, ..., $vN because the function concat() can have any number of arguments.
II. XPath 2.0 (XSLT 2.0)
'*'[current()/#flag = 'true']
And more generally, given 2*N atomic values $s1, $s2, ... $sN and $v1, $v2, ..., $vN, such that all $vi values are different, the result of evaluating this XPath 2.0 expression:
($s1, $s2, ..., $sN)[index-of(($v1, $v2, ..., $vN), $v)]
is $sK exactly when $v eq $vK.
You could utilise simple pattern matching in templates for this.
<xsl:template match="change[#flag = 'true']">
<xsl:template match="change">
So, the first one matches true entries, and the other one matches all other cases (which in your cases is just false
So, given the following stylesheet
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text" indent="yes"/>
<xsl:template match="change[#flag = 'true']">
<xsl:text>*
</xsl:text>
</xsl:template>
<xsl:template match="change">
<xsl:text>
</xsl:text>
</xsl:template>
<xsl:template match="#*|node()">
<xsl:apply-templates select="#*|node()"/>
</xsl:template>
</xsl:stylesheet>
When applied to your sample XML, the following is output
*
*

attributes construct error in Entity, line: 14

I have this xslt :
<xsl:param name="total_articles" select="3" />
<xsl:param name="articles_per_page" select="3" />
<xsl:apply-templates select="dagboek/entry[position > $offset][position < $articles_per_page+$offset]" >
<xsl:with-param name="total_pages" tunnel="yes">
<xsl:choose>
<xsl:when test="$value="2005-09" and $page="1">8</xsl:when>
<xsl:otherwise>floor(number($total_articles)-1) div $articles_per_page +1</xsl:otherwise>
</xsl:choose>
</xsl:with-param>
</xsl:apply-templates>
But now I get this error.
How can I calculate the outcome of the calculation and put it into the param total_pages.
Roelof
Edit 1: WHat I try to achieve is that if it's not 2005-09 and page is not 1 then the totalpages is calculated out of total_articles and articles_per_page. The outcome has to be put into the param pages.
<xsl:when test="$value="2005-09" and $page="1">8</xsl:when>
This isn't even well-formed XML document. The problem is with the nested quotes.
Probably you meant:
<xsl:when test="$value='2005-09' and $page='1' ">8</xsl:when>
Another possible problem: Tunnel parameters are only available in XSLT 2.0. You seem to be using XSLT 1.0
Update:
The OP in a comment has modified/clarified his initial question:
WHat I try to achieve is that if it's not 2005-09 and page is not 1
then the totalpages is calculated out of total_articles and
articles_per_page. The outcome has to be put into the param pages.
This can simply be expressed as:
<xsl:with-param name="total_pages" select=
"8*($value='2005-09' and $page='1')
+
(floor(number($total_articles)-1) div $articles_per_page +1)
*
not($value='2005-09' and $page='1')
"/>
Explanation:
We are using the fact that in XPath 1.0 by definition number($someBoolean) is 1 if $someBoolean is true() and is 0 if $someBoolean is false().
Therefore the pseudocode:
if($vCond)
then $vNum1
else $vNum2
can be expressed with a single XPath expression:
$vNum1*$vCond + $vNum2*not($vCond )
whenever a boolean is an argument to an arithmetic operator, it is automatically converted to a number.
So, here's what happens at run-time:
Suppose $vCond is true(), therefore not($vCond) is false().
Because $vCond and not($vCond) are arguments to the * operator, they are converted to numbers, respectively 1 and 0:
...
$vNum1*1 + $vNum2*0
This is equivalent to:
...
$vNum1*1
Note:
The above equivalence rule can be further generalized to N mutually exclusive conditions $vCond1, $vCond2, ..., $vCondN, and corresponding N values: $val1, $val2, ..., $valN:
$val1*$vCond1 + $val2*$vCond2 + ... + $valN*$vCondN
is equal to $valK (k in {1,..., N}) exactly when $vCondK is true()
You have a badly formed attribute:
<xsl:when test="$value="2005-09" and $page="1">8</xsl:when>
It should probably look like:
<xsl:when test="$value='2005-09' and $page='1'">8</xsl:when>
Is it this line?
<xsl:when test="$value="2005-09" and $page="1">8</xsl:when>
You're nesting double quotes - that's not going to work. Use a combination of single and double quotes :
<xsl:when test='$value="2005-09" and $page=1'>8</xsl:when>