XSLT count comma values count - xslt

I have a value like integer="1,2,3,4,5" in the xml. How can I count the total number using XSLT. So that the output gives me a count of 5
Regards,
Sam

Here's one way (there may be others). Simply translate all commas into empty strings, and then compare in difference in length of strings:
<xsl:value-of
select="string-length(#integer)
- string-length(translate(#integer, ',', '')) + 1" />
If you need to handle empty strings, try this instead
<xsl:value-of
select="string-length(#integer)
- string-length(translate(#integer, ',', ''))
+ 1 * (string-length(#integer) != 0)" />

If you want to count the comma-separated-values, but ALSO be able to reference the individual items, you can use a recursive template like such.
This XSLT 1.0 style-sheet will convert the comma-separated-values into nodes and then count them ...
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:msxsl="urn:schemas-microsoft-com:xslt">
<xsl:output method="text"/>
<xsl:template match="/">
<xsl:variable name="as-nodes">
<xsl:call-template name="parse-comma-separated-values">
<xsl:with-param name="csv" select="t/#csv" />
</xsl:call-template>
</xsl:variable>
<xsl:value-of select="count(msxsl:node-set($as-nodes)/*)" />
</xsl:template>
<xsl:template name="parse-comma-separated-values">
<xsl:param name="csv" />
<xsl:choose>
<xsl:when test="$csv = ''"/>
<xsl:when test="not( contains( $csv, ','))">
<value-node value="{$csv}" />
</xsl:when>
<xsl:otherwise>
<value-node value="{substring-before($csv,',')}" />
<xsl:call-template name="parse-comma-separated-values">
<xsl:with-param name="csv" select="substring-after($csv,',')"/>
</xsl:call-template>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
... when applied to this input document ...
<t csv="1,2,3,4,5"/>
... produces ...
5

Related

Order values in variable in xslt or whit

How can I order values in a variable that contains strings that are comma-separated?
It would be OK if the variable was separated on sub-strings of 001a and so on.
My variable is a string of values separated by commas, but because I join strings from more documents they are not in the right order. It is something like this:
001a, 001b, 001d, 100a, 100c, 100d, 001c, 001f, 100b,...
I would like to get this:
001a, 001b, 001c, 001d, 100a, 001b, 100c, 100d, 001f,...
Using XSL 2.0:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="2.0">
<xsl:variable name="unsorted" select="'001a, 001b, 001d, 100a, 100c, 100d, 001c, 001f, 100b'"/>
<xsl:variable name="sorted">
<xsl:for-each select="tokenize($unsorted, ', ')">
<xsl:sort select="." />
<xsl:choose>
<xsl:when test="position()!=last()">
<xsl:value-of select="."/><xsl:text>, </xsl:text>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="."/>
</xsl:otherwise>
</xsl:choose>
</xsl:for-each>
</xsl:variable>
<xsl:template match="/">
<unsorted><xsl:value-of select="$unsorted"/></unsorted>
<sorted><xsl:value-of select="$sorted"/></sorted>
</xsl:template>
</xsl:stylesheet>

Varying amount of iterations in XSL recursive loop within a for loop

I'm generating a CSV file from an XML using XSL. The XML contains Main elements with child elements Tags, which in turn contain varying amounts of child elements Tag. A part of the XML looks for example like this:
<Main>
<Tags>
<Tag>tag1</Tag>
<Tag>tag2</Tag>
<Tag>tag3</Tag>
</Tags>
</Main>
<Main>
<Tags>
<Tag>tag1</Tag>
<Tag>tag2</Tag>
<Tag>tag3</Tag>
<Tag>tag4</Tag>
<Tag>tag5</Tag>
<Tag>tag6</Tag>
</Tags>
</Main>
In the XSL I have a for each loop that goes through all my Main elements of my XML file. I want to print the values for all the Tag elements. I do this in another for-each loop which is inside the major loop. However, I always want to iterate 10 times, regardless of the amount of Tag elements. I want to print some text in each of the remaining iterations when I have exceeded the amount of printable Tag.
This is the output I'm after:
tag1,tag2,tag3,1,1,1,1,1,1,1,
tag1,tag2,tag3,tag4,tag5,tag6,1,1,1,1,
After the Tag for each loop, I'm calling a template, providing a variable with the amount of Tag in Tags. I then want the template to call itself recursively until it has done the varying amount of remaining iterations for the Tag elements of the current Main element. The amount of Tag elements changes with each Main iteration, which I suspect is a problem in my current solution (which causes my transformation software, Notepad++ with XML Tools, to crash):
<xsl:template match="/">
<xsl:for-each select="Main">
<xsl:for-each select="Tags/Tag">
<xsl:value-of select="Tag"/>
<xsl:text>,</xsl:text>
</xsl:for-each>
<xsl:call-template name="repeatable">
<xsl:with-param name="tagamount" select="count(Tags/*)"/>
</xsl:call-template>
<xsl:text>
</xsl:text>
</xsl:for-each>
</xsl:template>
<xsl:template name="repeatable">
<xsl:param name="tagamount"/>
<xsl:param name="index" select="0" />
<xsl:text>1,</xsl:text>
<xsl:if test="not($index = 10-$tagamount)">
<xsl:call-template name="repeatable">
<xsl:with-param name="index" select="$index + 1" />
</xsl:call-template>
</xsl:if>
</xsl:template>
Does anyone have any idea if it's possible to do this type of varying iteration, or am I out of luck?
Edit:
I managed to solve it. The problem was I had forgotten to pass on the variable tagamount with each recursive call. See my solution further below.
I couldn't wrap my head around your code. How about something simpler?
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text" encoding="UTF-8"/>
<xsl:variable name="sep" select="','"/>
<xsl:variable name="LF" select="'
'"/>
<xsl:variable name="filler" select="'1,2,3,4,5,6,7,8,9,10'"/>
<xsl:template match="/">
<xsl:for-each select="rt/Main/Tags">
<xsl:for-each select="Tag">
<xsl:value-of select="concat(., $sep)"/>
</xsl:for-each>
<xsl:value-of select="substring($filler, 2*count(Tag)+1)"/>
<xsl:value-of select="$LF"/>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
Note:
1. Your XML is missing a root element: I am using "rt" as a placeholder.
2. For testing purposes, I have changed "1,1,1,..." into "1,2.3...".
Here is one way to do it.
This XSLT stylesheet:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text"/>
<xsl:strip-space elements="*"/>
<!-- Sets the number of iterations per Tags element. -->
<xsl:variable name="maximum" select="10"/>
<!-- Matches all the Tags elements and calls a recursive template, intializing the count to 1. -->
<xsl:template match="//Tags">
<xsl:call-template name="output-tags">
<xsl:with-param name="count" select="1"/>
</xsl:call-template>
<xsl:text>
</xsl:text>
</xsl:template>
<!-- A recursive template that will repeat itself until its count reaches the maximum value.
If the count is equal to or less then the number of Tag elements inside the current Tags
element, then find the Tag element in the count position and print its value. Otherwise,
print 1. -->
<xsl:template name="output-tags">
<xsl:param name="count"/>
<xsl:if test="$count <= $maximum">
<xsl:choose>
<xsl:when test="$count <= count(Tag)">
<xsl:value-of select="Tag[count(preceding-sibling::Tag) = $count - 1]"/>
<xsl:text>,</xsl:text>
</xsl:when>
<xsl:otherwise>
<xsl:text>1,</xsl:text>
</xsl:otherwise>
</xsl:choose>
<xsl:call-template name="output-tags">
<xsl:with-param name="count" select="$count + 1"/>
</xsl:call-template>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
produces the following output when applied to your example input XML:
tag1,tag2,tag3,1,1,1,1,1,1,1,
tag1,tag2,tag3,tag4,tag5,tag6,1,1,1,1,
Thanks for the answers!
I managed to solve it right after I posted. The problem was I had forgotten to send the tagamount variable with the recursive call. After adding it, it works. The repeatable template then looks like this:
<xsl:param name="tagamount"/>
<xsl:param name="index" select="0" />
<xsl:text>1,</xsl:text>
<xsl:if test="not($index = 10-$tagamount)">
<xsl:call-template name="repeatable">
<xsl:with-param name="tagamount" select="$tagamount"/> <-----------
<xsl:with-param name="index" select="$index + 1" />
</xsl:call-template>
</xsl:if>

Split Attribute using ; as delimiter in XSLT

How to split an elements using ; as delimiter.my requirement is like below.
input:
<Element1>C:KEK39519US; U:085896395195; A:K39519US; B:S2345843</Element1>
output:
<CustItem>KEK39519US</CustItem>
<UNumber>085896395195</UNumber>
<ANumber>K39519US</ANumber>
<BNumber>S2345843</BNumber>
the input is every time not same.some times it comes like C:KEK39519US; U:085896395195; B:S2345843
some time like this C:KEK39519US; A:K39519US; B:S2345843
sometime like this U:085896395195; A:K39519US;
sometime like this C:KEK39519US; U:085896395195; A:K39519US;
To solve this in XSLT 1.0 you may need a named template which recursively calls itself. The template will process of the string before the first semi-colon, and output the element accordingly. It will then recursively call itself with the remaining part of the string after this semi-colon (if there is one)
Here is the full XSLT
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="Element1">
<xsl:call-template name="outputElements">
<xsl:with-param name="list" select="." />
</xsl:call-template>
</xsl:template>
<xsl:template name="outputElements">
<xsl:param name="list"/>
<xsl:variable name="first" select="normalize-space(substring-before(concat($list, ';'), ';'))"/>
<xsl:variable name="remaining" select="normalize-space(substring-after($list, ';'))"/>
<xsl:call-template name="createElement">
<xsl:with-param name="element" select="$first" />
</xsl:call-template>
<!-- If there are still elements left in the list, call the template recursively -->
<xsl:if test="$remaining">
<xsl:call-template name="outputElements">
<xsl:with-param name="list" select="$remaining"/>
</xsl:call-template>
</xsl:if>
</xsl:template>
<xsl:template name="createElement">
<xsl:param name="element"/>
<xsl:variable name="elementName">
<xsl:choose>
<xsl:when test="substring-before($element, ':') = 'C'">CustItem</xsl:when>
<xsl:otherwise><xsl:value-of select="concat(substring-before($element, ':'), 'Number')" /></xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:element name="{$elementName}">
<xsl:value-of select="substring-after($element, ':')" />
</xsl:element>
</xsl:template>
</xsl:stylesheet>
When applied to you XML, the following is output
<CustItem>KEK39519US</CustItem>
<UNumber>085896395195</UNumber>
<ANumber>K39519US</ANumber>
<BNumber>S2345843</BNumber>
Note the use of Attribute Value Templates in specifying the name of each new element.

Split attribute characters and output them using XSLT 1.0

I am in need to transform the below coding using XSLT 1.0 based on the separators attributes given. The text should be separated based on the separators given:
Input:
<chapter xmlns="http://www.w3.org/1998/Math/MathML">
<math display="inline"><mfenced separators=", : . ;"><mn>1</mn><mtext>b</mtext><mo>%</mo><mi>d</mi><mi>e</mi></mfenced></math>
<math display="inline"><mfenced separators=", ;"><mi>a</mi><mi>b</mi><mi>c</mi><mi>d</mi><mi>e</mi></mfenced></math>
<math display="inline"><mfenced separators=", : . ; ; : . ;"><mi>a</mi><mi>b</mi><mi>c</mi><mi>d</mi><mi>e</mi></mfenced></math>
</chapter>
output required:
1,b:%.d;e
a,b;c;d;e
a,b:c.d;e
Also please note that if there are too many separator characters, the extra ones are ignored. If separator characters are given, but there are too few, the last one is repeated as necessary
I could not able to get the output only if the separator characters are lesser than the child elements.
XSLT 1.0 tried:
<?xml version='1.0'?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:m="http://www.w3.org/1998/Math/MathML">
<xsl:template match="m:mfenced">
<xsl:variable name="text" select="#separators"/>
<xsl:for-each select="child::*">
<xsl:apply-templates/>
<xsl:choose>
<xsl:when test="contains($text,' ')">
<xsl:variable name="attr" select="string-length(translate($text, ' ', ''))"/>
<xsl:variable name="ch" select="count(parent::*/child::*)-1"/>
<xsl:if test="$ch=$attr"><xsl:value-of select="substring($text,count(preceding-sibling::*)+position(),1)"/></xsl:if>
<xsl:if test="$ch gt $attr">
<xsl:if test="not(substring($text,count(preceding-sibling::*)+position(),1)='')"><xsl:value-of select="substring($text,count(preceding-sibling::*)+position(),1)"/></xsl:if>
<xsl:if test="(substring($text,count(preceding-sibling::*)+position(),1)='')"><xsl:value-of select="substring($text,count(preceding-sibling::*)+1,1)"/></xsl:if>
</xsl:if>
<xsl:if test="$ch lt $attr and count(following-sibling::*)>0"><xsl:value-of select="substring($text,count(preceding-sibling::*)+position(),1)"/></xsl:if>
</xsl:when>
<xsl:otherwise><xsl:if test="count(following-sibling::*)>0"><xsl:value-of select="$text"/></xsl:if></xsl:otherwise></xsl:choose>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
The following solution is based on obtaining the position of each <m:mi> within the <m:fenced> elements to obtain the next operator to be outputted.
Note. I am assuming that the string length used to represent each operator is 1.
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:m="http://www.w3.org/1998/Math/MathML">
<xsl:output method="text" />
<!-- Ignore all text nodes (just for demo) -->
<xsl:template match="text()" />
<xsl:template match="m:mfenced">
<!-- Print children values and operators -->
<xsl:apply-templates select="*" mode="list-op">
<xsl:with-param name="separator-str" select="#separators" />
<xsl:with-param name="separator-len" select="string-length(#separators)" />
</xsl:apply-templates>
<!-- Print new line -->
<xsl:text>
</xsl:text>
</xsl:template>
<!-- Last m:mi elements for each m:mfenced are just printed -->
<xsl:template match="*[last()]" mode="list-op">
<xsl:value-of select="."/>
</xsl:template>
<!-- In this template we use the position() function to calculate the next
operator that is going to be outputted -->
<xsl:template match="*" mode="list-op">
<xsl:param name="separator-str" />
<!-- This parameter is not required, but allows us to cache
the length of the separators string instead of calculating it
for each m:mi element -->
<xsl:param name="separator-len" />
<!-- Print current value -->
<xsl:value-of select="." />
<!-- Calculate the separator position within the string -->
<xsl:variable name="string-position" select="2*position() - 1" />
<!-- Check if the position oveflows the position in the array, and
if it does, print the last separator in the string. -->
<xsl:choose>
<xsl:when test="$separator-len >= $string-position">
<xsl:value-of select="substring($separator-str, $string-position, 1)" />
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="substring($separator-str, $separator-len)" />
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>

How to show a character n times in XSLT?

I have a template with a parameter. How can I insert a tab character n times?
n is the value of the parameter.
In XSLT 2.0:
<xsl:for-each select="1 to $count"> </xsl:for-each>
(Sadly though, I suspect that if you were using XSLT 2.0 you wouldn't need to ask the question).
Another technique often used with XSLT 1.0 is the hack:
<xsl:for-each select="//*[position() <= $count]"> </xsl:for-each>
which works provided the number of elements in your source document is greater than the number of tab characters you want to output.
Just call it recursively; output a tab, then call the same template again with n-1 passed in, if n > 1.
<xsl:template name="repeat">
<xsl:param name="output" />
<xsl:param name="count" />
<xsl:if test="$count > 0">
<xsl:value-of select="$output" />
<xsl:call-template name="repeat">
<xsl:with-param name="output" select="$output" />
<xsl:with-param name="count" select="$count - 1" />
</xsl:call-template>
</xsl:if>
</xsl:template>
As has been pointed out, this example will actually output a minimum of one. In my experience where the output is whitespace, it's usually needed. You can adapt the principle of a recursive template like this any way you see fit.
This seems the simplest and most flexible to me.
For XSLT 1.0 (or perhaps 1.1).
<xsl:variable name="count">10</xsl:variable>
<xsl:variable name="repeat"><xsl:text> </xsl:text></xsl:variable>
<xsl:sequence select="string-join((for $i in 1 to $count return $repeat),'')"/>
Of course the count variable is where you assign your n parameter.
I used the variable repeat to hold the tab character, but you could just replace the $repeat with the tab character in single quotes in the sequence element. Note: This variable can be of a length greater than 1, which creates a whole bunch of possibilities.
It does not use recursion, so it won't run into a recursion limit.
I don't know the maximum value you can use for count, but I tested it up to 10,000.
Globally define a long enough array of tabs:
<xsl:variable name="TABS" select="' '" />
Then use like this:
<xsl:value-of select="fn:substring($TABS, 1, fn:number($COUNT))" />
(XSLT 1.0)
<xsl:template name="tabs">
<xsl:param name="n"/>
<xsl:if test="$n > 0"> <!-- When n = 0, output nothing. -->
<xsl:call-template name="tabs"> <!-- Recursive call: call same template... -->
<xsl:with-param name="n" select="$n - 1"/> <!-- ... for writing n - 1 tabs. -->
</xsl:call-template>
<xsl:text> </xsl:text> <!-- Add one tab character. -->
</xsl:if>
</xsl:template>
Example usage:
<xsl:call-template name="tabs">
<xsl:with-param name="n" select="3"/>
</xsl:call-template>
I've discovered an LGPL-licensed library for doing this called functx, as I was sure someone had to have already done this... This is a "standard library" type XSLT library, which contains a function called repeat-string. From the docs:
The functx:repeat-string function returns a string consisting of a given number of copies of $stringToRepeat concatenated together.
Where I use it like this in my code:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:functx="http://www.functx.com">
<xsl:import href="../buildlib/functx-1.0.xsl"/>
<xsl:output omit-xml-declaration="yes" />
<xsl:variable name="INDENT" select="' '" />
....
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*" />
</xsl:copy>
</xsl:template>
<xsl:template match="data-pusher-properties">
<xsl:for-each select="property">
<xsl:choose>
...
<xsl:when test="boolean(#value = '${pusher.notifications.server}')">
<xsl:value-of select="functx:repeat-string($INDENT, #indent)" />
<xsl:text>"</xsl:text>
<xsl:value-of select="#name" />
<xsl:text>": </xsl:text>
<xsl:text>"</xsl:text>
<xsl:value-of select="$pusher.notifications.email.server" />
<xsl:text>"\
</xsl:text>
</xsl:when>
...
</xsl:choose>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
So for printing a tab character n times, call it like this:
<xsl:value-of select="functx:repeat-string(' ', n)" />
I know this question is old, but I hope this can still help someone.
Documentation for the repeat-string function