XSL Basics: showing value of a boolean? - xslt

When I have this in xsl:
<xsl:choose>
<xsl:when test="something > 0">
<xsl:variable name="myVar" select="true()"/>
</xsl:when>
<xsl:otherwise>
<xsl:variable name="myVar" select="false()"/>
</xsl:otherwise>
</xsl:choose>
How can I then print out the value of "myVar"? Or more importantly, how can I use this boolean in another choose statement?

<xsl:choose>
<xsl:when test="something > 0">
<xsl:variable name="myVar" select="true()"/>
</xsl:when>
<xsl:otherwise>
<xsl:variable name="myVar" select="false()"/>
</xsl:otherwise>
</xsl:choose>
This is quite wrong and useless, because the variable $myVar goes out of scope immediately.
One correct way to conditionally assign to the variable is:
<xsl:variable name="myVar">
<xsl:choose>
<xsl:when test="something > 0">1</xsl:when>
<xsl:otherwise>0</xsl:otherwise>
</xsl:choose>
</xsl:variable>
However, you really don't need this -- much simpler is:
<xsl:variable name="myVar" select="something > 0"/>
How can I then print out the value of "myVar"?
Use:
<xsl:value-of select="$myVar"/>
Or more importantly, how can I use this boolean in another choose
statement?
Here is a simple example:
<xsl:choose>
<xsl:when test="$myVar">
<!-- Do something -->
</xsl:when>
<xsl:otherwise>
<!-- Do something else -->
</xsl:otherwise>
</xsl:choose>
And here is a complete example:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:template match="/*/*">
<xsl:variable name="vNonNegative" select=". >= 0"/>
<xsl:value-of select="name()"/>: <xsl:text/>
<xsl:choose>
<xsl:when test="$vNonNegative">Above zero</xsl:when>
<xsl:otherwise>Below zero</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
when this transformation is applied on the following XML document:
<temps>
<Monday>-2</Monday>
<Tuesday>3</Tuesday>
</temps>
the wanted, correct result is produced:
Monday: Below zero
Tuesday: Above zero

Related

Splitting XSLT variable in to a foreach loop (XSLT 1.0) [duplicate]

how to split a node value in XSLT 1.0?
<mark>1,2</mark>
i need to perform some operations in the for loop with each value of the output of split.
<xsl:for-each select="">
</xsl:for-each>
How to do this?
I. XSLT 1.0 solution:
Here is one way to do this in XSLT 1.0 using only the xxx:node-set() extension function:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:ext="http://exslt.org/common" exclude-result-prefixes="ext">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:template match="mark">
<xsl:variable name="vrtfSplit">
<xsl:apply-templates/>
</xsl:variable>
<xsl:for-each select="ext:node-set($vrtfSplit)/*">
<processedItem>
<xsl:value-of select="10 * ."/>
</processedItem>
</xsl:for-each>
</xsl:template>
<xsl:template match="text()" name="split">
<xsl:param name="pText" select="."/>
<xsl:if test="string-length($pText) >0">
<item>
<xsl:value-of select=
"substring-before(concat($pText, ','), ',')"/>
</item>
<xsl:call-template name="split">
<xsl:with-param name="pText" select=
"substring-after($pText, ',')"/>
</xsl:call-template>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
when this transformation is applied to the following XML document:
<mark>1,2,3,4,5</mark>
The wanted, correct output (each item multiplied by 10) is produced:
<processedItem>10</processedItem>
<processedItem>20</processedItem>
<processedItem>30</processedItem>
<processedItem>40</processedItem>
<processedItem>50</processedItem>
II. XSLT 2.0 solution:
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="xs">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:template match="mark">
<xsl:for-each select="tokenize(., ',')">
<processedItem>
<xsl:sequence select="10*xs:integer(.)"/>
</processedItem>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
The explaination by Dimitre Novatchev is awesome, but we can also do it in much more simpler way without using node-set() function have a look:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:variable name="delimiter">
<xsl:text>,</xsl:text>
</xsl:variable>
<xsl:template match="mark">
<xsl:variable name="dataList">
<xsl:value-of select="."/>
</xsl:variable>
<xsl:call-template name="processingTemplate">
<xsl:with-param name="datalist" select="$dataList"/>
</xsl:call-template>
</xsl:template>
<xsl:template name="processingTemplate">
<xsl:param name="datalist"/>
<xsl:choose>
<xsl:when test="contains($datalist,$delimiter) ">
<xsl:element name="processedItem">
<xsl:value-of select="substring-before($datalist,$delimiter) * 10"/>
</xsl:element>
<xsl:call-template name="processingTemplate">
<xsl:with-param name="datalist" select="substring-after($datalist,$delimiter)"/>
</xsl:call-template>
</xsl:when>
<xsl:when test="string-length($datalist)=1">
<xsl:element name="processedItem">
<xsl:value-of select="$datalist * 10"/>
</xsl:element>
</xsl:when>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
In 1.0 you need to write a recursive template - except you don't, because it's already been written. Download the str:tokenize template from http://www.exslt.org.
If you can use exslt there's a tokenize() function that will do this nicely.
node-set str:tokenize(string, string?)
See http://www.exslt.org/str/functions/tokenize/
This code will split a delimited string in XSLT 1.0
(It will work for 2.0, but don't use the node-set.)
It will also optionally suppress empty elements in the string
or optionally upper case the elements.
<!-- Example delimited string. -->
<xsl:variable name="delimitedString" select="'a, b, c, , , d, e, f, g'"/>
<!-- Create a node set where each node contains one of the elements from the
delimited string. -->
<xsl:variable name="splitNodes">
<xsl:call-template name="getNodeListFromDelimitedList">
<xsl:with-param name="inStrList" select="$delimitedString"/>
<xsl:with-param name="delimiter" select="','"/>
<xsl:with-param name="suppressEmptyElements" select="false()"/>
<xsl:with-param name="upperCase" select="false()"/>
<xsl:with-param name="allTrim" select="false()"/>
</xsl:call-template>
</xsl:variable>
<!-- Use this for XSLT 1.0 only. -->
<xsl:variable name="splitNodesList" select="msxml:node-set($splitNodes)"/>
<!-- Use the split node list to do something. For example, create a string like
the delimited string, but without the delimiters. -->
<xsl:variable name="nonDelimitedString">
<xsl:for-each select="$splitNodesList/element">
<xsl:value-of select="."/>
</xsl:for-each>
</xsl:variable>
<!-- Do something with the nonDelimitedString. -->
<!--
*****************************************************************************************
This template converts a delimited string list to a node list as follows:
Each value in the delimited input string is extracted from the string. Then, a node is
created to contain the value. The name of the node is 'element', and it is added to the
list. To use this template, create an variable and call this template from within the variable.
If you are using XSLT version 1.0, convert the node list to a node set using the node-set
function. You can access the element as follows: $SomeVariableNodeSet/element
*****************************************************************************************
-->
<xsl:template name="getNodeListFromDelimitedList">
<!-- Delimited string with one or more delimiters. -->
<xsl:param name="inStrList"/>
<!-- The delimiter. -->
<xsl:param name="delimiter" select="'|'"/>
<!-- Set to true to suppress empty elements from being added to node list. Otherwise, set to 'false'.-->
<xsl:param name="suppressEmptyElements" select="true()"/>
<!-- Set to true to upper case the strings added to the node list. -->
<xsl:param name="upperCase" select="false()"/>
<!-- Set to true to left trim and right trim the strings added to the nodes list. -->
<xsl:param name="allTrim" select="false()"/>
<xsl:variable name="element">
<xsl:choose>
<xsl:when test="contains($inStrList,$delimiter)">
<xsl:value-of select="substring-before($inStrList,$delimiter)"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$inStrList"/>
</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<!-- Write out the element based on parameters. -->
<xsl:if test="not($suppressEmptyElements) or normalize-space($element) != ''">
<!-- Put the element in the list. -->
<xsl:element name="element">
<xsl:choose>
<xsl:when test="$allTrim">
<xsl:call-template name="all-trim">
<xsl:with-param name="inStr" select="$element"/>
<xsl:with-param name="upperCase" select="$upperCase"/>
</xsl:call-template>
</xsl:when>
<xsl:when test="$upperCase">
<xsl:value-of select="translate($element, 'abcdefghijklmnopqrstuvwxyz', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ')"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$element"/>
</xsl:otherwise>
</xsl:choose>
</xsl:element>
</xsl:if>
<xsl:if test="contains($inStrList,$delimiter)">
<!-- Call template recursively to process the next element. -->
<xsl:call-template name="getNodeListFromDelimitedList">
<xsl:with-param name="inStrList" select="substring-after($inStrList,$delimiter)"/>
<xsl:with-param name="delimiter" select="$delimiter"/>
<xsl:with-param name="suppressEmptyElements" select="$suppressEmptyElements"/>
<xsl:with-param name="upperCase" select="$upperCase"/>
<xsl:with-param name="allTrim" select="$allTrim"/>
</xsl:call-template>
</xsl:if>
</xsl:template>
<!--
*****************************************************************************************
This template trims the blanks from the left and right sides of a string.
*****************************************************************************************
-->
<xsl:template name="all-trim">
<!-- The string that you want to all trim. -->
<xsl:param name="inStr"/>
<xsl:param name="upperCase" select="false()"/>
<xsl:variable name="leftTrimmed">
<xsl:call-template name="left-trim">
<xsl:with-param name="inStr" select="$inStr"/>
</xsl:call-template>
</xsl:variable>
<xsl:variable name="rightTrimmed">
<xsl:call-template name="right-trim">
<xsl:with-param name="inStr" select="$leftTrimmed"/>
</xsl:call-template>
</xsl:variable>
<xsl:choose>
<xsl:when test="$upperCase">
<xsl:value-of select="translate($rightTrimmed, 'abcdefghijklmnopqrstuvwxyz', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ')"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$rightTrimmed"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<!--
*****************************************************************************************
This template trims the blanks from the left side of a string.
*****************************************************************************************
-->
<xsl:template name="left-trim">
<!-- The string you want to left trim. -->
<xsl:param name ="inStr"/>
<xsl:choose>
<xsl:when test="$inStr!=''">
<xsl:variable name="temp" select="substring($inStr, 1, 1)"/>
<xsl:choose>
<xsl:when test="$temp=' '">
<xsl:choose>
<xsl:when test="string-length($inStr) > 1">
<xsl:call-template name="left-trim">
<xsl:with-param name="inStr" select="substring($inStr, 2, string-length($inStr)-1)"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="''"/>
</xsl:otherwise>
</xsl:choose>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$inStr"/>
</xsl:otherwise>
</xsl:choose>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="''"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<!--
*****************************************************************************************
This template trims the blanks from the right side of a string.
*****************************************************************************************
-->
<xsl:template name="right-trim">
<!-- The string you want to right trim. -->
<xsl:param name ="inStr"/>
<xsl:choose>
<xsl:when test="$inStr!=''">
<xsl:variable name="temp" select="substring($inStr, string-length($inStr), 1)"/>
<xsl:choose>
<xsl:when test="$temp=' '">
<xsl:choose>
<xsl:when test="string-length($inStr) > 1">
<xsl:call-template name="right-trim">
<xsl:with-param name="inStr" select="substring($inStr, 1, string-length($inStr)-1)"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="''"/>
</xsl:otherwise>
</xsl:choose>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$inStr"/>
</xsl:otherwise>
</xsl:choose>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="''"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
Based on #Abhinav solution I just simplified recursive solution to work with general strings. My input string which I need to split is "GEN_EME2_G9_3311|A55;GEN_EME2_G9_3312|A55;foooo_3312|A42"
<xsl:variable name="delimiter">
<xsl:text>;</xsl:text>
</xsl:variable>
<xsl:template name="fooTemplate">
...
<xsl:choose>
<xsl:when test="$conditionlink != ''">
<xsl:call-template name="processconditionlinktemplate">
<xsl:with-param name="datalist" select="$conditionlink"/>
</xsl:call-template>
</xsl:when>
</xsl:choose>
...
</xsl:template>
<xsl:template name="processconditionlinktemplate">
<xsl:param name="datalist"/>
<xsl:choose>
<xsl:when test="contains($datalist,$delimiter)">
<xsl:element name="processedItem">
<xsl:value-of select="substring-before($datalist,$delimiter)"/>
</xsl:element>
<xsl:call-template name="processconditionlinktemplate">
<xsl:with-param name="datalist" select="substring-after($datalist,$delimiter)"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:element name="processedItem">
<xsl:value-of select="$datalist"/>
</xsl:element>
</xsl:otherwise>
</xsl:choose>
</xsl:template>

XSLT using regex pattern

I have a requirements to get the value based on a priority #schemeNames. Get the value of ID if the #schemeName='TaxNumber' is present, else if #schemeName='PassportNumber', else if #schemeName is no value. After getting the value, it needs to check or ignore the 1st 2 characters if it is Alpha. Also, I need to consider the spaces between words in #schemeName. If for example, the value of my #schemeName is 'Tax Number' or 'taxnumber' it is valid. But if the value is like this, 't axNum Ber', it should not validate this value.
Here is my XSLT:
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:template match="/">
<Result>
<xsl:for-each select="/Record/Data/ID">
<xsl:choose>
<xsl:when test="matches(lower-case(.[#schemeName]),'^tax\s+number')">
<xsl:if test="matches(substring(.,1,2),'^[a-zA-Z]+$')">
<xsl:value-of select="substring(.,3)"/>
</xsl:if>
</xsl:when>
<xsl:when test="matches(lower-case(.[#schemeName]),'^passport\s+number')">
<xsl:if test="matches(substring(.,1,2),'^[a-zA-Z]+$')">
<xsl:value-of select="substring(.,3)"/>
</xsl:if>
</xsl:when>
<xsl:when test=".[#schemeName='']">
<xsl:if test="matches(substring(.,1,2),'^[a-zA-Z]+$')">
<xsl:value-of select="substring(.,3)"/>
</xsl:if>
</xsl:when>
</xsl:choose>
</xsl:for-each>
</Result>
</xsl:template>
</xsl:stylesheet>
INPUT:
<Record>
<Data>
<ID schemeName="TaxNumber">PT123457</ID>
<ID schemeName="PassportNumber">PT098732</ID>
<ID schemeName="LicenseNumber">PT445423</ID>
<ID schemeName="">PT7566435</ID>
</Data>
</Record>
GENERATED OUTPUT:
<Result>7566435</Result>
The output generated is coming from the #schemeName that is null. It should be coming from the TaxNumber since it is present. There's something wrong in my condition when checking the #schemeNames.
I am using XSLT v2.0. Thank you!
How about setting your priorities in variables? Something like:
<xsl:variable name="prio1" select="ID[matches(lower-case(#schemeName),'^tax\s?number')]"/>
<xsl:variable name="prio1_value">
<xsl:choose>
<xsl:when test="matches($prio1, '^[A-z]{2}.*$')">
<xsl:value-of select="substring($prio1,3)"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$prio1"/>
</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:variable name="prio2" select="ID[matches(lower-case(#schemeName),'^passport\s?number')]"/>
<xsl:variable name="prio2_value">
<xsl:choose>
<xsl:when test="matches($prio2, '^[A-z]{2}.*$')">
<xsl:value-of select="substring($prio2,3)"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$prio2"/>
</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:variable name="prio3" select="ID[#schemeName='']"/>
<xsl:variable name="prio3_value">
<xsl:choose>
<xsl:when test="matches($prio3, '^[A-z]{2}.*$')">
<xsl:value-of select="substring($prio3,3)"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$prio3"/>
</xsl:otherwise>
</xsl:choose>
</xsl:variable>
Note: I have changed \s+ to \s? to make the space optional.
Also, you can use the if-then-else construct in xslt 2.0. The final code is below:
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:template match="/">
<xsl:apply-templates select="Record/Data"/>
</xsl:template>
<xsl:template match="Data">
<xsl:variable name="prio1" select="ID[matches(lower-case(#schemeName),'^tax\s?number')]"/>
<xsl:variable name="prio1_value">
<xsl:choose>
<xsl:when test="matches($prio1, '^[A-z]{2}.*$')">
<xsl:value-of select="substring($prio1,3)"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$prio1"/>
</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:variable name="prio2" select="ID[matches(lower-case(#schemeName),'^passport\s?number')]"/>
<xsl:variable name="prio2_value">
<xsl:choose>
<xsl:when test="matches($prio2, '^[A-z]{2}.*$')">
<xsl:value-of select="substring($prio2,3)"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$prio2"/>
</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:variable name="prio3" select="ID[#schemeName='']"/>
<xsl:variable name="prio3_value">
<xsl:choose>
<xsl:when test="matches($prio3, '^[A-z]{2}.*$')">
<xsl:value-of select="substring($prio3,3)"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$prio3"/>
</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<Result>
<xsl:value-of select="if ($prio1) then ($prio1_value) else
if ($prio2) then ($prio2_value) else
if ($prio3) then ($prio3_value) else 0"/>
</Result>
</xsl:template>
</xsl:stylesheet>

How to refer node created by one template in XSLT at runtime from the calling template?

I have split one string key6='||1-LIW-3324|1-LIW-3325|1-LIW-3326|1-LIW-3327|1-LIW-4232' with separator '|' thru one template named StringSplit.
I have called this StringSplit template from another template. So each split value is stored in SeparatedLineItems.
Now I want to refer each split node(SeparatedLineItems) in the calling template and want to compare each separated/split node with another variable . How can i do it.
Here is the code.
<xsl:template match="/">
<xsl:variable name="check3">
<xsl:value-of select="1-LIW-3"/>
</xsl:variable>
<myProj>
<xsl:call-template name="StringSplit">
<xsl:with-param name="val" select="$key6"/>
</xsl:call-template>
<!--here I want to compare "SeperatedLineItems" with $check3 -->
<!--<xsl:variable name="con">
<xsl:value-of select="contains($key6,$check3)"/>
</xsl:variable>-->
</myProj>
</xsl:template>
<xsl:template name="StringSplit">
<xsl:param name="val" select="$key6"/>
<!-- do a check to see if the input string (still) has a "|" in it -->
<xsl:choose>
<xsl:when test="contains($val, '|')">
<!-- pull out the value of the string before the "|" delimiter -->
<SeperatedLineItems>
<xsl:value-of select="substring-before($val, '|')"/>
</SeperatedLineItems>
<!-- recursively call this template and pass in value AFTER the "|" delimiter -->
<xsl:call-template name="StringSplit">
<xsl:with-param name="val" select="substring-after($val, '|')"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<!-- if there is no more delimiter values, print out the whole string -->
<SeperatedLineItems>
<xsl:value-of select="$val"/>
</SeperatedLineItems>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
This XSLT 1.0 style-sheet demonstrates how to access the elements returned by calling a template ...
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:msxsl="urn:schemas-microsoft-com:xslt"
exclude-result-prefixes="xsl msxsl">
<xsl:output method="text"/>
<xsl:variable name="key6" select="'||1-LIW-3324|1-LIW-3325|1-LIW-3326|1-LIW-3327|1-LIW-4232'" />
<xsl:template match="/">
Does my $key6 variable contain precisely '1-LIW-3'?
<xsl:variable name="keys">
<xsl:call-template name="StringSplit">
<xsl:with-param name="val" select="$key6"/>
</xsl:call-template>
</xsl:variable>
<xsl:choose>
<xsl:when test="msxsl:node-set($keys)/*[. = '1-LIW-3']">
YES
</xsl:when>
<xsl:otherwise>
NO
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template name="StringSplit">
<xsl:param name="val" />
<xsl:choose>
<xsl:when test="contains($val,'|')">
<SeperatedLineItems>
<xsl:value-of select="substring-before($val,'|')" />
</SeperatedLineItems>
<xsl:variable name="remainder">
<xsl:call-template name="StringSplit">
<xsl:with-param name="val" select="substring-after($val,'|')" />
</xsl:call-template>
</xsl:variable>
<xsl:copy-of select="msxsl:node-set($remainder)/*" />
</xsl:when>
<xsl:when test="$val != ''">
<SeperatedLineItems>
<xsl:value-of select="$val" />
</SeperatedLineItems>
</xsl:when>
<xsl:otherwise>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
To which the result is...
Does my $key6 variable contain '1-LIW-3'?
NO
And here is the XSLT 2.0 equivalent...
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="xsl xs">
<xsl:output method="text"/>
<xsl:variable name="key6" select="'||1-LIW-3324|1-LIW-3325|1-LIW-3326|1-LIW-3327|1-LIW-4232'" />
<xsl:template match="/">
Does my $key6 variable contain precisely '1-LIW-3'?
<xsl:variable name="keys" select="tokenize($key6,'\|')" as="xs:string*" />
<xsl:choose>
<xsl:when test="some $key in $keys satisfies ($key = '1-LIW-3')">
YES
</xsl:when>
<xsl:otherwise>
NO
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
The above makes the $keys variable a sequence of strings rather than elements containing strings. If you prefer elements, then you could use the following alternative. Although this alternative suffers from the disadvantage that it does not include the empty string items.
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="xsl xs">
<xsl:output method="text"/>
<xsl:variable name="key6" select="'||1-LIW-3324|1-LIW-3325|1-LIW-3326|1-LIW-3327|1-LIW-4232'" />
<xsl:template match="/">
Does my $key6 variable contain precisely '1-LIW-3'?
<xsl:variable name="keys" as="element()*" >
<xsl:analyze-string select="concat($key6,'|')" regex="([^\|]*)\|">
<xsl:matching-substring>
<SeperatedLineItems>
<xsl:value-of select="regex-group(1)" />
</SeperatedLineItems>
</xsl:matching-substring>
</xsl:analyze-string>
</xsl:variable>
<xsl:choose>
<xsl:when test="$keys[. = '1-LIW-3']">
YES
</xsl:when>
<xsl:otherwise>
NO
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>

How to select the smallest value from a bunch of variables?

Assume I have variables $a, $b, $c and $d which all hold numbers. I would like to get the smallest (largest) value. My typical XSLT 1.0 approach to this is
<xsl:variable name="minimum">
<xsl:for-each select="$a | $b | $c | $d">
<xsl:sort
select="."
data-type="number"
order="ascending" />
<xsl:if test="position()=1"><xsl:value-of select="." /></xsl:if>
</xsl:for-each>
</xsl:variable>
However, my xslt 1.0 processor complains with
runtime error: file stylesheet.xslt line 106 element for-each
The 'select' expression does not evaluate to a node set.
How can I compute the minimum (maximum) of the given values?
Of course, I could use a long series of <xsl:when> statements and check all combinations, but I'd rather like a smaller solution.
If the variables have statically defined values (not dynamically computed), then something like the following can be done with XSLT 1.0:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text"/>
<xsl:variable name="vA" select="3"/>
<xsl:variable name="vB" select="1"/>
<xsl:variable name="vC" select="9"/>
<xsl:variable name="vD" select="5"/>
<xsl:template match="/">
<xsl:for-each select=
"document('')/*/xsl:variable
[contains('|vA|vB|vC|vD|', concat('|', #name, '|'))]
/#select
">
<xsl:sort data-type="number" order="ascending"/>
<xsl:if test="position() = 1">
Smallest: <xsl:value-of select="."/>
</xsl:if>
<xsl:if test="position() = last()">
Largest: <xsl:value-of select="."/>
</xsl:if>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
When this transformation is applied on any XML document (not used), the wanted, correct result is produced:
Smallest: 1
Largest: 9
II. Now, suppose the variables are dynamically defined.
We can do something like this (but need the xxx:node-set() extension function):
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:ext="http://exslt.org/common">
<xsl:output method="text"/>
<xsl:variable name="vA" select="number(/*/*[3])"/>
<xsl:variable name="vB" select="number(/*/*[1])"/>
<xsl:variable name="vC" select="number(/*/*[9])"/>
<xsl:variable name="vD" select="number(/*/*[5])"/>
<xsl:template match="/">
<xsl:variable name="vrtfStore">
<num><xsl:value-of select="$vA"/></num>
<num><xsl:value-of select="$vB"/></num>
<num><xsl:value-of select="$vC"/></num>
<num><xsl:value-of select="$vD"/></num>
</xsl:variable>
<xsl:for-each select="ext:node-set($vrtfStore)/*">
<xsl:sort data-type="number" order="ascending"/>
<xsl:if test="position() = 1">
Smallest: <xsl:value-of select="."/>
</xsl:if>
<xsl:if test="position() = last()">
Largest: <xsl:value-of select="."/>
</xsl:if>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
when this transformation is applied on the following XML document:
<nums>
<num>01</num>
<num>02</num>
<num>03</num>
<num>04</num>
<num>05</num>
<num>06</num>
<num>07</num>
<num>08</num>
<num>09</num>
<num>10</num>
</nums>
the wanted, correct result is produced:
Smallest: 1
Largest: 9
This XSLT 1.0 solution uses recursive templates to parse a delimited list of values to return the min/max value from the list.
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:variable name="a" select="'3'"/>
<xsl:variable name="b" select="'1'"/>
<xsl:variable name="c" select="'9'"/>
<xsl:variable name="d" select="'5'"/>
<xsl:template match="/">
<xsl:text>
Smallest: </xsl:text>
<xsl:call-template name="min">
<xsl:with-param name="values" select="concat($a,',',$b,',',$c,',',$d)"/>
</xsl:call-template>
<xsl:text>
Largest: </xsl:text>
<xsl:call-template name="max">
<xsl:with-param name="values" select="concat($a,',',$b,',',$c,',',$d)"/>
</xsl:call-template>
</xsl:template>
<xsl:template name="min">
<xsl:param name="values" />
<xsl:param name="delimiter" select="','"/>
<xsl:param name="min"/>
<xsl:variable name="currentValue" >
<xsl:choose>
<xsl:when test="contains($values, $delimiter)">
<xsl:value-of select="substring-before($values,$delimiter)"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$values"/>
</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:variable name="minimumValue">
<xsl:choose>
<xsl:when test="$min and $min > $currentValue">
<xsl:value-of select="$currentValue"/>
</xsl:when>
<xsl:when test="$min">
<xsl:value-of select="$min"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$currentValue" />
</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:choose>
<xsl:when test="substring-after($values,$delimiter)">
<xsl:call-template name="min">
<xsl:with-param name="min" select="$minimumValue" />
<xsl:with-param name="values" select="substring-after($values,$delimiter)" />
<xsl:with-param name="delimiter" select="$delimiter"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$minimumValue" />
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template name="max">
<xsl:param name="values" />
<xsl:param name="delimiter" select="','"/>
<xsl:param name="max"/>
<xsl:variable name="currentValue" >
<xsl:choose>
<xsl:when test="contains($values, $delimiter)">
<xsl:value-of select="substring-before($values,$delimiter)"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$values"/>
</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:variable name="maximumValue">
<xsl:choose>
<xsl:when test="$max and $currentValue > $max">
<xsl:value-of select="$currentValue"/>
</xsl:when>
<xsl:when test="$max">
<xsl:value-of select="$max"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$currentValue" />
</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:choose>
<xsl:when test="substring-after($values,$delimiter)">
<xsl:call-template name="max">
<xsl:with-param name="max" select="$maximumValue" />
<xsl:with-param name="values" select="substring-after($values,$delimiter)" />
<xsl:with-param name="delimiter" select="$delimiter"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$maximumValue" />
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
When executed, produces the following output:
Smallest: 1
Largest: 9
I've never had to do this in 1.0 (I use 2.0), but you could do this:
<xsl:variable name="minimum">
<xsl:choose>
<xsl:when test="$b > $a and $c > $a and $d > $a"><xsl:value-of select="$a"/></xsl:when>
<xsl:when test="$a > $b and $c > $b and $d > $b"><xsl:value-of select="$b"/></xsl:when>
<xsl:when test="$b > $c and $a > $c and $d > $c"><xsl:value-of select="$c"/></xsl:when>
<xsl:when test="$b > $d and $c > $d and $a > $d"><xsl:value-of select="$d"/></xsl:when>
</xsl:choose>
</xsl:variable>
There's got to be a better way though.

XSLT big integer (int64) handling msxml

When trying to do math on an big integer (int64) large number in xslt template I get the wrong result since there is no native 64-bit integer support in xslt (xslt number is 64-bit double). I am using msxml 6.0 on Windows XP SP3. Are there any work around for this on Windows?
<tables>
<table>
<table_schem>REPADMIN</table_schem>
<table_name>TEST_DESCEND_IDENTITY_BIGINT</table_name>
<column>
<col_name>COL1</col_name>
<identity>
<col_min_val>9223372036854775805</col_min_val>
<col_max_val>9223372036854775805</col_max_val>
<autoincrementvalue>9223372036854775807</autoincrementvalue>
<autoincrementstart>9223372036854775807</autoincrementstart>
<autoincrementinc>-1</autoincrementinc>
</identity>
</column>
</table>
</tables>
This test returns true due to the inexact representation of the large integer in 64-bit double (I am assuming) but actually is false if I could tell the xslt processor somehow to use int64 rather than the default 64-bit double for the number data since big integer is the actual data type for numbers in the xml input.
<xsl:when test="autoincrementvalue =
(col_min_val + autoincrementinc)">
<xsl:value-of select="''"/>
</xsl:when>
here is the complete template
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" >
<!--Reseed Derby identity column-->
<xsl:output omit-xml-declaration='yes' method='text' />
<xsl:param name="stmtsep">;</xsl:param>
<xsl:param name="schemprefix"></xsl:param>
<xsl:template match="tables">
<xsl:variable name="identitycount" select="count(table/column/identity)"></xsl:variable>
<xsl:for-each select="table/column/identity">
<xsl:variable name="table_schem" select="../../table_schem"></xsl:variable>
<xsl:variable name="table_name" select="../../table_name"></xsl:variable>
<xsl:variable name="tablespec">
<xsl:if test="$schemprefix">
<xsl:value-of select="$table_schem"/>.</xsl:if><xsl:value-of
select="$table_name"/></xsl:variable>
<xsl:variable name="col_name" select="../col_name"></xsl:variable>
<xsl:variable name="newstart">
<xsl:choose>
<xsl:when test="autoincrementinc > 0">
<xsl:choose>
<xsl:when test="col_max_val = '' and
autoincrementvalue = autoincrementstart">
<xsl:value-of select="''"/>
</xsl:when>
<xsl:when test="col_max_val = ''">
<xsl:value-of select="autoincrementstart"/>
</xsl:when>
<xsl:when test="autoincrementvalue =
(col_max_val + autoincrementinc)">
<xsl:value-of select="''"/>
</xsl:when>
<xsl:when test="(col_max_val + autoincrementinc) <
autoincrementstart">
<xsl:value-of select="autoincrementstart"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="col_max_val + autoincrementinc"/>
</xsl:otherwise>
</xsl:choose>
</xsl:when>
<xsl:when test="autoincrementinc < 0">
<xsl:choose>
<xsl:when test="col_min_val = '' and
autoincrementvalue = autoincrementstart">
<xsl:value-of select="''"/>
</xsl:when>
<xsl:when test="col_min_val = ''">
<xsl:value-of select="autoincrementstart"/>
</xsl:when>
<xsl:when test="autoincrementvalue =
(col_min_val + autoincrementinc)">
<xsl:value-of select="''"/>
</xsl:when>
<xsl:when test="(col_min_val + autoincrementinc) >
autoincrementstart">
<xsl:value-of select="autoincrementstart"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="col_min_val + autoincrementinc"/>
</xsl:otherwise>
</xsl:choose>
</xsl:when>
</xsl:choose>
</xsl:variable>
<xsl:if test="not(position()=1)"><xsl:text>
</xsl:text></xsl:if>
<xsl:choose>
<!--restart with ddl changes both the next identity value AUTOINCREMENTVALUE and
the identity start number AUTOINCREMENTSTART eventhough in this casewe only want
to change only the next identity number-->
<xsl:when test="$newstart != '' and
$newstart != autoincrementvalue">alter table <xsl:value-of
select="$tablespec"/> alter column <xsl:value-of
select="$col_name"/> restart with <xsl:value-of
select="$newstart"/><xsl:if test="$identitycount>1">;</xsl:if></xsl:when>
<xsl:otherwise>-- reseed <xsl:value-of select="$tablespec"/> is not necessary</xsl:otherwise>
</xsl:choose>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
Are there any work around for this on
Windows?
No, unless you use an XSLT 2.0 processor, such as Saxon or AltovaXML.
In XSLT 2.0 XPath 2.0 is used, which has support for xs:decimal and this gives you the required precision. With Saxon one can also use just xs:integer, because both Saxon and Altova implement Big Integer arithmetic.
Here is an XSLT 2.0 stylesheet (that uses xs:integer by default):
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xsl:output method="text"/>
<xsl:template match="/">
<xsl:value-of select=
"9223372036854775805 + (-1)"/>
</xsl:template>
</xsl:stylesheet>
Both Saxon 9.x and AltovaXML2010 produce the following correct result:
9223372036854775804
Here is an XSLT 2.0 stylesheet that uses xs:decimal explicitly:
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xsl:output method="text"/>
<xsl:template match="/">
<xsl:value-of select=
"xs:decimal(9223372036854775805) + (-1)"/>
</xsl:template>
</xsl:stylesheet>
Both Saxon 9.x and AltovaXML2010 again produce the correct result