How to show a character n times in XSLT? - 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

Related

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.

how to set a variable name dynamic in xsl

following is my code where i want to give the hidden variable and assign the value for it to access later.
<xsl:for-each select="//Root/Record">
<xsl:if test="(#CIMtrek_accountlist_customer_number != '') ">
<option style="padding:5px;">
<xsl:attribute name="class">>
<xsl:choose>
<xsl:when test="(position() mod 2) = 0">
AlternateRowOne
</xsl:when>
<xsl:otherwise>
AlternateRowTwo
</xsl:otherwise>
</xsl:choose>
</xsl:attribute>
// here i want to set hidden varialble and assign the value for it
<xsl:attribute name="value">
<xsl:value-of
select="#CIMtrek_accountlist_customer_number" /></xsl:attribute>
<xsl:value-select="#CIMtrek_accountlist_customer_number" />
</option>
</xsl:if>
</xsl:for-each>
the variable name will be some thing similar to like this
<input type="hidden"
name="hdnDialogListCtrlPlaceholder_CIMtrek_DailyshipCo_Destination_"+i
id="hdnDialogListCtrlPlaceholder_CIMtrek_DailyshipCo_Destination_"+i
/>
where i =0
and i++;
name_1
name_2
name_n
is it possible to use : <FieldRef Name="<FieldInternalName>" Explicit="TRUE"/>
how to do this in xsl
I typically achieve recursion in the following manner.
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:template match="/">
<xsl:call-template name="recur">
<xsl:with-param name="max_recursions" select="5"/>
</xsl:call-template>
</xsl:template>
<xsl:template name="recur">
<xsl:param name="n">0</xsl:param>
<xsl:param name="max_recursions"/>
REPEATING UNIT HERE
<xsl:if test="$max_recursions != $n">
<xsl:call-template name="recur">
<xsl:with-param name="n" select="$n + 1"/>
<xsl:with-param name="max_recursions" select="$max_recursions"/>
</xsl:call-template>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
It isn't often advised though and you can usually achieve much faster, more succinct and more readable code through a well constructed XPath.
You're trying to write procedural code in XSLT, and that won't work. If you explain what transformation you want to perform (what's the input, what's the output, and how are they related?) then we can show you how to do it "the XSLT way", that is, declaratively.

XSLT count comma values count

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

XSLT : Looping from 1 to 60

What is the best way to loop in XSLT from 1 to 60?
I research in net, there are some templates to do this, is there any other way for example like a built-in function?
In XSLT 2.0,
<xsl:for-each select="1 to 60">...</xsl:for-each>
But I guess that you must be using XSLT 1.0, otherwise you wouldn't be asking.
In XSLT 1.0 you should use recursion: a template that calls itself with a counter that's incremented on each call, and the recursion terminates when the required value is reached.
Alternatively there's a workaround in XSLT 1.0: provided your source document contains at least 60 nodes, you can do
<xsl:for-each select="(//node())[60 >= position()]">...</xsl:for-each>
The problem with simple recursion when processing long sequences is that often the space for the call stack becomes insufficient and the processing ends due to stack overflow. This typically happens with sequence length >= 1000.
A general technique to avoid this (implementable with any XSLT processor, even if it doesn't recognize tail-recursion) is DVC (Divide and Conquer) style recursion.
Here is an example of a transformation that successfully prints the numbers from 1 to 1000000 (1M):
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text"/>
<xsl:template match="/">
<xsl:call-template name="displayNumbers">
<xsl:with-param name="pStart" select="1"/>
<xsl:with-param name="pEnd" select="1000000"/>
</xsl:call-template>
</xsl:template>
<xsl:template name="displayNumbers">
<xsl:param name="pStart"/>
<xsl:param name="pEnd"/>
<xsl:if test="not($pStart > $pEnd)">
<xsl:choose>
<xsl:when test="$pStart = $pEnd">
<xsl:value-of select="$pStart"/>
<xsl:text>
</xsl:text>
</xsl:when>
<xsl:otherwise>
<xsl:variable name="vMid" select=
"floor(($pStart + $pEnd) div 2)"/>
<xsl:call-template name="displayNumbers">
<xsl:with-param name="pStart" select="$pStart"/>
<xsl:with-param name="pEnd" select="$vMid"/>
</xsl:call-template>
<xsl:call-template name="displayNumbers">
<xsl:with-param name="pStart" select="$vMid+1"/>
<xsl:with-param name="pEnd" select="$pEnd"/>
</xsl:call-template>
</xsl:otherwise>
</xsl:choose>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
When applied on any XML document (not used) this transformation produces the wanted result -- all the numbers from 1 to 1000000.
You can use/adapt this transformation for any task that needs to "do something N times".
Very simple check inside the foreach-loop
<xsl:if test="$maxItems > position()">
do something
</xsl:if>
Based on Dimitre Novatchev's answer.
Example:
<xsl:variable name="maxItems" select="10" />
<xsl:variable name="sequence" select="any-sequence"/>
<xsl:for-each select="$sequence">
<!-- Maybe sort first -->
<xsl:sort select="#sort-by" order="descending" />
<!-- where the magic happens -->
<xsl:if test="$maxItems > position()">
do something
</xsl:if>
</xsl:for-each>
The basic example for V1.0 using recursion would it be like this:
<xsl:template match="/">
<Root>
<!-- Main Call to MyTemplate -->
<xsl:call-template name="MyTemplate" />
</Root>
</xsl:template>
<xsl:template name="MyTemplate">
<xsl:param name="index" select="1" />
<xsl:param name="maxValue" select="60" />
<MyCodeHere>
<xsl:value-of select="$index"/>
</MyCodeHere>
<!-- < represents "<" for html entities -->
<xsl:if test="$index < $maxValue">
<xsl:call-template name="MyTemplate">
<xsl:with-param name="index" select="$index + 1" />
<xsl:with-param name="total" select="$maxValue" />
</xsl:call-template>
</xsl:if>
</xsl:template>
XSLT works based on templates and you'll need a template do run that loop.
You'll need to build a template receiving start and end values and, inside it, make a recursive call computing with start + 1. When $start equals $end, you do return your template, without another call.
In practice: http://www.ibm.com/developerworks/xml/library/x-tiploop/index.html