XSLT 1.0 Substring from right side of string - xslt-1.0

I have a value in an element as 433554567643.
I would like to change it to 43 35545 67643. The grouping should start from right side of the value.
Is it possible to use subtring from end to start of the value?
Thanks.

You could do this with a recursive template.
<xsl:template name="add-spaces">
<xsl:param name="group" select="5" />
<xsl:param name="text" />
<xsl:if test="string-length($text) > $group">
<xsl:call-template name="add-spaces">
<xsl:with-param name="group" select="$group" />
<xsl:with-param name="text"
select="substring($text, 1, string-length($text) - $group)" />
</xsl:call-template>
<xsl:text> </xsl:text>
</xsl:if>
<xsl:value-of select="substring($text, string-length($text) - $group + 1)" />
</xsl:template>
You would call this when required using
<xsl:call-template name="add-spaces">
<xsl:with-param name="text" select="'433554567643'" />
<!-- or select="path/to/element" as appropriate -->
</xsl:call-template>

If your value is always a number, you could use format-number() with a pattern that groups the numbers by 5 digits and then translate() the "," into " ":
<xsl:value-of select="translate(format-number('433554567643', '#,#####'),
',', ' ')" />

Related

How to remove duplicate characters in string?

I have a string that looks like this: some, , , , text
I need it to look like this: some, text
There is unknown number of such commas, it may be 2, it may be 10
I tried recursive approach but it did not work as expected:
<xsl:template name="remove-duplicate">
<xsl:param name="text" />
<xsl:param name="remove" />
<xsl:value-of select="$text" />
<xsl:if test="contains($text, $remove)">
<xsl:call-template name="string-replace-all">
<xsl:with-param name="text" select="$text" />
<xsl:with-param name="replace" select="$remove" />
<xsl:with-param name="by" select="''" />
</xsl:call-template>
<xsl:call-template name="remove-duplicate">
<xsl:with-param name="text" select="$text" />
<xsl:with-param name="remove" select="$remove" />
</xsl:call-template>
</xsl:if>
</xsl:template>
Calling the template:
<xsl:call-template name="remove-duplicate">
<xsl:with-param name="text" select="string(.)" />
<xsl:with-param name="remove" select="' ,'" />
</xsl:call-template>
The string-replace-all was stolen from this question: XSLT string replace
How about:
<xsl:value-of select="translate(normalize-space(translate($yourString, ', ', ' …')), ' …', ', ')"/>
Note that this assumes your string does not contain any whitespace characters other than spaces.
If this assumption is not true, try the solution posted here: https://stackoverflow.com/a/38177946/3016153

I am trying to get XSL to render an XML attribute with an & tag as &amp

I have an XML tag which has names in it e.g. H&M. The requirement is that it should output as H &amp M. I am unable to achieve this yet.
None of the escape characters, & or & worked for me. Here's what worked:
<xsl:template name="string-replace-all">
<xsl:param name="text" />
<xsl:choose>
<xsl:when test="contains($text, '&')">
<xsl:value-of select="substring-before($text,'&')"/>
<xsl:text disable-output-escaping="yes"><![CDATA[&]]></xsl:text>
<xsl:value-of select="substring-after($text,'&')"/>
</xsl:when>
<xsl:when test="contains($text, '&apos;')">
<xsl:value-of select="substring-before($text,'&apos;')"/>
<xsl:text disable-output-escaping="yes"><![CDATA[&apos;]]></xsl:text>
<xsl:value-of select="substring-after($text,'&apos;')"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$text" />
</xsl:otherwise>
</xsl:choose>
</xsl:template>
And then call template:
<Nm>
<xsl:variable name="suppNm">
<xsl:call-template name="string-replace-all">
<xsl:with-param name="text" select="Payee/Name" />
</xsl:call-template>
</xsl:variable>
<xsl:value-of select="$suppNm" />
</Nm>
If anyone knows a better solution, would be very interested to know. I am a total beginner to XSL.

How to replace single apostrophe to double apostrophe in XSLT 1.0?

I need to replace a single quote to double quotes.
For example: input = Ram's
So i need a output as Ram''s
How to do in xslt 1.0? Please help me!
Its working for me!
<xsl:template match="name">
<xsl:copy>
<xsl:call-template name="replace">
<!-- Here getting the replaced values -->
<xsl:with-param name="text" select="."/>
</xsl:call-template>
</xsl:copy>
</xsl:template>
<xsl:template name="replace">
<xsl:param name="text"/>
<xsl:param name="searchString">'</xsl:param>
<xsl:param name="replaceString">''</xsl:param>
<xsl:choose>
<xsl:when test="contains($text,$searchString)">
<xsl:value-of select="substring-before($text,$searchString)"/>
<xsl:value-of select="$replaceString"/>
<!-- recursive call -->
<xsl:call-template name="replace">
<xsl:with-param name="text" select="substring-after($text,$searchString)"/>
<xsl:with-param name="searchString" select="$searchString"/>
<xsl:with-param name="replaceString" select="$replaceString"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$text"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>

how to provide variable dynamic value from other template?

<xsl:template name="split">
<xsl:param name="list"/>
<xsl:variable name="first">
<xsl:value-of select="substring-before($list,' ')"/>
</xsl:variable>
<xsl:copy-of select="$first"/>
</xsl:template>
<xsl:variable name="test">c0 c1 c2 c3 c4</xsl:variable>
<xsl:variable name="var2>
<xsl:call-template name="split">
<xsl:with-param name="returnvalue">
<xsl:value-of select="$test"></xsl:with-param>
</xsl:call-template>
</xsl:variable>
// processing done
i want to return value from template as c0 then back to template match do processing then again go to split template again return c1 done same processing then back to split template then again processing in match template; depending upon the value of test variable...
How could i retreive these values one by one and process the code..??
This stylesheet will scan forward over your input string:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">
<xsl:output method="text"/>
<xsl:variable name="string" select="'c0 c1 c2 c3 c4'"/>
<xsl:variable name="delim" select="' '"/>
<xsl:template match="/">
<xsl:call-template name="wrapper">
<xsl:with-param name="string" select="$string"/>
</xsl:call-template>
</xsl:template>
<xsl:template name="wrapper">
<xsl:param name="string"/>
<xsl:choose>
<!-- handle empty input -->
<xsl:when test=" $string = '' "/>
<!-- handle next token -->
<xsl:when test="contains($string, $delim)">
<xsl:call-template name="process">
<xsl:with-param name="substring" select="substring-before($string,$delim)"/>
</xsl:call-template>
<xsl:call-template name="wrapper">
<xsl:with-param name="string" select="substring-after($string,$delim)"/>
</xsl:call-template>
</xsl:when>
<!-- handle last token -->
<xsl:otherwise>
<xsl:call-template name="process">
<xsl:with-param name="substring" select="$string"/>
</xsl:call-template>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template name="process">
<xsl:param name="substring"/>
<xsl:text>RECEIVED SUBSTRING: </xsl:text>
<xsl:value-of select="$substring"/>
<xsl:text>
</xsl:text>
</xsl:template>
</xsl:stylesheet>
to produce the following output:
RECEIVED SUBSTRING: c0
RECEIVED SUBSTRING: c1
RECEIVED SUBSTRING: c2
RECEIVED SUBSTRING: c3
RECEIVED SUBSTRING: c4

XSL iterate over attribute and ignore some elements

I need to do rather simple thing of iterating over elements in my XML(more explanation under the XML)
<form>
<field index="1" name="field_X_1" group="firstGroup" type="String">
<value>Value of Field X 1 of first group</value>
</field>
<field index="1" name="field_Y_1" group="firstGroup" type="String">
<value>Value of Field Y 1 of first group</value>
</field>
<field index="2" name="field_X_2" group="firstGroup" type="String">
<value>Value of Field X 2 of first group</value>
</field>
<field index="2" name="field_Y_2" group="firstGroup" type="String">
<value>Value of Field Y 2 of first group</value>
</field>
<field index="1" name="field_A_1" group="secondGroup" type="String">
<value>Value of Field A 1 of second group</value>
</field>
<field index="1" name="field_B_1" group="secondGroup" type="String">
<value>Value of Field B 1 of second group</value>
</field>
<field index="2" name="field_A_2" group="secondGroup" type="String">
<value>Value of Field A 2 of second group</value>
</field>
<field index="2" name="field_B_2" group="secondGroup" type="String">
<value>Value of Field B 2 of second group</value>
</field>
</form>
Right now I am iterating over ALL fields of firstGroup and everytime I found that index has changed(by comparing index to index of sibbling) I add new line character).
Problem is I need only to iterate over index (but still keep distiction between firstGroup and secondGroup).
My output should look like
Value of Field X 1 of first group, Value of Field Y 1 of first group
Value of Field X 2 of first group, Value of Field Y 2 of first group
Value of Field A 1 of second group, Value of Field B 1 of second group
Value of Field A 2 of second group, Value of Field B 2 of second group
etc
My XSL now looks like
<xsl:for-each select='/form/field[#group="firstGroup"]'>
<xsl:sort select="#index" order="ascending"></xsl:sort>
<xsl:if test='preceding-sibling::*[#group="firstGroup"][1]/#index != #index and count(preceding-sibling::*) != 0 '>
<xsl:text>
</xsl:text>
</xsl:if>
<xsl:value-of select="//field[#name=concat('field_X_', #index,')]/value"/>
<xsl:value-of select="//field[#name=concat('field_Y_', #index,')]/value"/>
</xsl:for-each>
<!-- Differente iteration for second group-->
<xsl:text>
</xsl:text>
<xsl:for-each select='/form/field[#group="firstGroup"]'>
<xsl:sort select="#index" order="ascending"></xsl:sort>
<xsl:if test='preceding-sibling::*[#group="firstGroup"][1]/#index != #index and count(preceding-sibling::*) != 0 '>
<xsl:text>
</xsl:text>
</xsl:if>
<xsl:value-of select="//field[#name=concat('field_A_', #index,')]/value"/>
<xsl:value-of select="//field[#name=concat('field_B_', #index,')]/value"/>
</xsl:for-each>
<xsl:output method="text" />
<xsl:template match="form">
<!--* find all the groups *-->
<xsl:variable name="groups">
<xsl:call-template name="get-group-names">
<xsl:with-param name="nodes" select="field" />
</xsl:call-template>
</xsl:variable>
<xsl:call-template name="process-all-groups">
<xsl:with-param name="group-names" select="$groups" />
</xsl:call-template>
</xsl:template>
<xsl:template name="get-group-names">
<xsl:param name="nodes" />
<xsl:param name="result-so-far" />
<xsl:choose>
<xsl:when test="not($nodes)">
<xsl:value-of select="$result-so-far" />
</xsl:when>
<xsl:when test="contains(
concat(' ', $result-so-far),
concat(' ', $nodes[1]/#group, ' '))" >
<xsl:call-template name="get-group-names">
<xsl:with-param name="nodes" select="$nodes[position() > 1]" />
<xsl:with-param name="result-so-far" select="$result-so-far" />
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:call-template name="get-group-names">
<xsl:with-param name="nodes" select="$nodes[position() > 1]" />
<xsl:with-param name="result-so-far"
select="concat($result-so-far, $nodes[1]/#group, ' ')" />
</xsl:call-template>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template name="process-all-groups">
<xsl:param name="group-names" />
<xsl:variable name="group" select="substring-before($group-names, ' ')"/>
<xsl:if test="not(string-length($group) = 0)">
<xsl:call-template name="index">
<xsl:with-param name="n" select="1" />
<xsl:with-param name="group" select="$group" />
</xsl:call-template>
<xsl:call-template name="process-all-groups">
<xsl:with-param name="group-names"
select="substring-after($group-names, ' ')" />
</xsl:call-template>
</xsl:if>
</xsl:template>
<xsl:template name="index">
<xsl:param name="n" select="1" />
<xsl:param name="group" />
<xsl:variable name="with-n"
select="/form/field[#group = $group][#index = $n]" />
<xsl:if test="$with-n">
<xsl:for-each select="$with-n">
<xsl:sort use="#index" order="ascending" />
<xsl:value-of select="value" />
<xsl:if test="not(position() = last())">
<xsl:text>,</xsl:text>
</xsl:if>
</xsl:for-each>
<xsl:text>
</xsl:text>
<xsl:call-template name="index">
<xsl:with-param name="n" select="$n + 1" />
<xsl:with-param name="group" select="$group" />
</xsl:call-template>
</xsl:if>
</xsl:template>
<xsl:template match="field"></xsl:template>