How to replace commas with spaces in XSL - xslt

I need to replace every other comma with a space in my XML output. Right now, I have latitude and longitude that looks like this:
-0.52437106918239,0.391509433962264,-0.533805031446541,0.430817610062893,0
-0.547955974842767,0.427672955974843,
I need the coordinates in my XML output to look like this:
-0.52437106918239 0.391509433962264, -0.533805031446541 0.430817610062893,0
-0.547955974842767 0.427672955974843
How can I use XSLT to do this? Here is my xsl:
<?xml version="1.0" encoding="ISO-8859-1"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:kml="http://www.opengis.net/kml/2.2">
<xsl:output method="text"/>
<xsl:template match="/">
<xsl:apply-templates select="kml:kml/kml:Document/kml:Placemark/kml:Polygon
/kml:outerBoundaryIs/kml:LinearRing"/>
</xsl:template>
<xsl:template match="kml:LinearRing">
"POLYGON((<xsl:value-of select="kml:coordinates"/>))"
</xsl:template>
</xsl:stylesheet>

In XSLT 2.0 it would be trivial. You could use replace().
In XSLT 1.0, you could use template like so. Call the convert-space template on your list that needs every second comma replaced.
<xsl:template name="convert-space">
<xsl:param name="text"/>
<xsl:choose>
<xsl:when test="contains($text,',')">
<xsl:value-of select="substring-before($text,',')"/>
<xsl:value-of select="' '"/>
<xsl:call-template name="convert-comma">
<xsl:with-param name="text" select="substring-after($text,',')"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$text"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template name="convert-comma">
<xsl:param name="text"/>
<xsl:choose>
<xsl:when test="contains($text,',')">
<xsl:value-of select="substring-before($text,',')"/>
<xsl:value-of select="','"/>
<xsl:call-template name="convert-space">
<xsl:with-param name="text" select="substring-after($text,',')"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$text"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>

Related

XSLT Replace Empty Node with Dummy

My requirement is as below
1) Replace Space with %20
2) Replace / with %2F
3) empty elements with value as dummy
Sample Input:
<?xml version="1.0" encoding="UTF-8" ?>
<process xmlns="http://xmlns.oracle.com/CCS/Project5/BPELProcess1">
<Sender>Sender 1</Sender>
<TransactionId>TransactionId/2</TransactionId>
<TransactionType>TransactionType5</TransactionType>
<Status>Status6</Status>
<Limit>70.73</Limit>
<Remarks>Remarks8</Remarks>
<Result>GlobalResult9</Result>
<Type>DecisionType10</Type>
<DecidedBy>DecidedBy11</DecidedBy>
<AddRequest1/>
<AddRequest2>RAMA</AddRequest2>
</process>
Required Output:
<process xmlns="http://xmlns.oracle.com/CCS/Project5/BPELProcess1">
<Sender>Sender%201</Sender>
<TransactionId>TransactionId%2F2</TransactionId>
<TransactionType>TransactionType5</TransactionType>
<Status>Status6</Status>
<Limit>70.73</Limit>
<Remarks>Remarks8</Remarks>
<Result>GlobalResult9</Result>
<Type>DecisionType10</Type>
<DecidedBy>DecidedBy11</DecidedBy>
<AddRequest1>DUMMY</AddRequest1>
<AddRequest2>RAMA</AddRequest2>
</process>
I have tried below XSLT but could not find a wat replace empty node to Dummy
eg
<AddRequest1></AddRequest1> is not coverted to <AddRequest1>DUMMY</AddRequest1>
XSLT tried is as below
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="//text()">
<xsl:call-template name="rep_SPLChar">
<xsl:with-param name="text" select="."/>
</xsl:call-template>
</xsl:template>
<xsl:template name="rep_SPLChar">
<xsl:param name="text"/>
<xsl:variable name="temp_space" select="'%20'"/>
<xsl:variable name="temp_backslash" select="'%2F'"/>
<xsl:if test="normalize-space($text) != ''">
<xsl:choose>
<xsl:when test="contains($text,' ')">
<xsl:call-template name="rep_SPLChar">
<xsl:with-param name="text"
select="concat((concat(substring-before($text,' '),$temp_space)),substring-after($text,' '))"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:choose>
<xsl:when test="contains($text,'/')">
<xsl:call-template name="rep_SPLChar">
<xsl:with-param name="text"
select="concat((concat(substring- before($text,'/'),$temp_backslash)),substring-after($text,'/'))"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$text"/>
</xsl:otherwise>
</xsl:choose>
</xsl:otherwise>
</xsl:choose>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
Try adding this template to your XSLT to match "empty" elements
<xsl:template match="*[not(*)][not(normalize-space())]">
<xsl:copy>
<xsl:apply-templates select="#*"/>
<xsl:text>DUMMY</xsl:text>
</xsl:copy>
</xsl:template>

Split by delimiter using xslt 1.0

This is my XML:
<LIGHT_DESCRIPTION>
<LIGHT_CHARACTERISTICS>Q+LFl</LIGHT_CHARACTERISTICS>
<LIGHT_SIGNAL_GROUP>(6)(1)</LIGHT_SIGNAL_GROUP>
</LIGHT_DESCRIPTION>
For me is necessary split by character '+' :
at Q is referred --> (6)
at LF1 is referred --> (1)
This is the output using XSLT 1.0:
Q (6) - LF1 (1)
Try it this way?
XSLT 1.0
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text" encoding="UTF-8"/>
<xsl:template match="/LIGHT_DESCRIPTION">
<xsl:call-template name="tokenize">
<xsl:with-param name="names" select="LIGHT_CHARACTERISTICS"/>
<xsl:with-param name="values" select="LIGHT_SIGNAL_GROUP"/>
</xsl:call-template>
</xsl:template>
<xsl:template name="tokenize">
<xsl:param name="names"/>
<xsl:param name="values"/>
<xsl:choose>
<xsl:when test="contains($names, '+')">
<xsl:value-of select="substring-before($names, '+')"/>
<xsl:text> </xsl:text>
<xsl:value-of select="substring-before($values, ')')"/>
<xsl:text>) - </xsl:text>
<!-- recursive call -->
<xsl:call-template name="tokenize">
<xsl:with-param name="names" select="substring-after($names, '+')"/>
<xsl:with-param name="values" select="substring-after($values, ')')"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="concat($names, ' ', $values)"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
Note: IMHO, you are exchanging one bad format for another just as bad. Why don't you take this opportunity to produce output in an established format, such as .csv for example?

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>

Adding numbers in a string using XSLT

I have a string (in a variable) that has a list of numbers separated by space or comma.
I need to sum the numbers in the string.
example string "1,2,5,12,3"
or "1 2 5 12 3"
Is there a way to add the numbers within the string and return the total?
This much shorter transformation:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text"/>
<xsl:template match="text()" name="sumStringList">
<xsl:param name="pText" select="."/>
<xsl:param name="pSum" select="0"/>
<xsl:param name="pDelim" select="','"/>
<xsl:choose>
<xsl:when test="not(string-length($pText) >0)">
<xsl:value-of select="$pSum"/>
</xsl:when>
<xsl:otherwise>
<xsl:variable name="vnewList"
select="concat($pText,$pDelim)"/>
<xsl:variable name="vHead" select=
"substring-before($vnewList, $pDelim)"/>
<xsl:call-template name="sumStringList">
<xsl:with-param name="pText" select=
"substring-after($pText, $pDelim)"/>
<xsl:with-param name="pSum" select="$pSum+$vHead"/>
<xsl:with-param name="pDelim" select="$pDelim"/>
</xsl:call-template>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
when applied on the following XML document:
<t>1,2,5,12,3</t>
produces the wanted, correct result:
23
Explanation: Recursively called named template that also matches a text node. A sentinel (appended comma) is added to speed up and streamline processing.
II. XSLT 2.0 solution:
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text"/>
<xsl:param name="pDelim" select="','"/>
<xsl:template match="text()">
<xsl:sequence select=
"sum(for $s in tokenize(.,$pDelim)
return number($s)
)
"/>
</xsl:template>
</xsl:stylesheet>
When applied on the same XML document (above), this transformation produces the same wanted, correct answer:
23
Here we use the standard XPath 2.0 function tokenize() and we must convert every resulting token to number (using the number() function) before finally applying the standard XPath function sum().
I don't know XSLT, but generally you would split the string using spaces and commas as separators.
After a quick search I found that you can use tokenize(string, separator) as the split function if you are using XSLT 2.0. This page has an example on how to use tokenize.
Here is an XSLT 1.0 solution
<?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:variable name="listOfValues" select="'1,2,5,12,3'" />
<xsl:call-template name="splitAndAdd">
<xsl:with-param name="list" select="$listOfValues"/>
</xsl:call-template>
</xsl:template>
<xsl:template name="splitAndAdd">
<xsl:param name="list" />
<xsl:param name="delimiter" select="','"/>
<xsl:param name="total" select="0" />
<xsl:variable name="newList">
<xsl:choose>
<xsl:when test="contains($list, $delimiter)">
<xsl:value-of select="normalize-space($list)"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="concat(normalize-space($list),$delimiter)" />
</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:variable name="token"
select="substring-before($newList, $delimiter)" />
<xsl:variable name="remaining"
select="normalize-space(substring-after($newList, $delimiter))" />
<xsl:variable name="newTotal" select="$total + number($token)" />
<xsl:choose>
<xsl:when test="$remaining">
<xsl:call-template name="splitAndAdd">
<xsl:with-param name="delimiter" select="$delimiter"/>
<xsl:with-param name="list" select="$remaining"/>
<xsl:with-param name="total" select="$newTotal" />
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$newTotal" />
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>

How to parse a delimited string using the index of the delimiter in xslt?

I have a delimited string which looks like below
FirstName&LastName&Address1&Address2&City&State&country&
I need to split the string based on the index of occurrence of the delimiter.
Say, if i need the first four values in the string, i should get the output in
two different variables, where the first variable should have the string with
first four values and the second variable having the remaining string.
I don't want to use exslt or any other extensions.
Please help me with a simple solution.
Input:
<xsl:variable name="index" select='4' />
<xsl:variable name="delimitedString" select='FirstName&LastName&Address1&Address2&City&State&country&' />
Output:
<xsl:variable name="requiredString">
FirstName&LastName&Address1&Address2&
</xsl:variable>
<xsl:variable name="remainingString">
City&State&country&
</xsl:variable>
This transformation:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:param name="pSplitIndex" select="4"/>
<xsl:template match="text()" name="splitAt">
<xsl:param name="pLeft" select="''"/>
<xsl:param name="pRight" select="."/>
<xsl:param name="pSplitAt" select="$pSplitIndex"/>
<xsl:param name="pDelim" select="'&'"/>
<xsl:choose>
<xsl:when test=
"not(string-length($pRight))
or
not(contains($pRight, $pDelim))
">
<t1>
<xsl:value-of select="concat($pLeft, $pRight)"/>
</t1>
<t2/>
</xsl:when>
<xsl:otherwise>
<xsl:choose>
<xsl:when test="$pSplitAt = 0">
<t1><xsl:value-of select="$pLeft"/></t1>
<t2><xsl:value-of select="$pRight"/></t2>
</xsl:when>
<xsl:otherwise>
<xsl:call-template name="splitAt">
<xsl:with-param name="pLeft" select=
"concat($pLeft, substring-before($pRight, $pDelim), $pDelim)
"/>
<xsl:with-param name="pRight"
select="substring-after($pRight, $pDelim)"/>
<xsl:with-param name="pSplitAt" select="$pSplitAt -1"/>
<xsl:with-param name="pDelim" select="$pDelim"/>
</xsl:call-template>
</xsl:otherwise>
</xsl:choose>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
when applied on this XML document:
<t>FirstName&LastName&Address1&Address2&City&State&country&</t>
produces the required two strings that result from splitting /t/text() at the fourth & delimiter:
<t1>FirstName&LastName&Address1&Address2&</t1>
<t2>City&State&country&</t2>
You can define a variable, say vrtfSplitResult whose body is the result of applying templates to the text node containing the string. Then, if you can use the xxx:node-set() extension, the two variables you want to define are:
<xsl:variable name="requiredString"
select="xxx:node-set($vrtfSplitResult)/*/t1">
<xsl:variable name="remainingString"
select="xxx:node-set($vrtfSplitResult)/*/t2">
In case you are not allowed to use even the xxx:node-set() extension function, then you should use a similar, named template: getStartingString. This has pretty much the logic of the splitAt template above:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:param name="pSplitIndex" select="4"/>
<xsl:template match="text()">
<xsl:variable name="vrequiredString">
<xsl:call-template name="getStartingString"/>
</xsl:variable>
<xsl:variable name="vremainingString" select=
"substring-after(.,$vrequiredString)"/>
<t>
<t1><xsl:value-of select="$vrequiredString"/></t1>
<t2><xsl:value-of select="$vremainingString"/></t2>
</t>
</xsl:template>
<xsl:template name="getStartingString">
<xsl:param name="pLeft" select="''"/>
<xsl:param name="pRight" select="."/>
<xsl:param name="pSplitAt" select="$pSplitIndex"/>
<xsl:param name="pDelim" select="'&'"/>
<xsl:choose>
<xsl:when test=
"not(string-length($pRight))
or
not(contains($pRight, $pDelim))
">
<xsl:value-of select="$pLeft"/>
</xsl:when>
<xsl:otherwise>
<xsl:choose>
<xsl:when test="$pSplitAt = 0">
<xsl:value-of select="$pLeft"/>
</xsl:when>
<xsl:otherwise>
<xsl:call-template name="getStartingString">
<xsl:with-param name="pLeft" select=
"concat($pLeft, substring-before($pRight, $pDelim), $pDelim)
"/>
<xsl:with-param name="pRight"
select="substring-after($pRight, $pDelim)"/>
<xsl:with-param name="pSplitAt" select="$pSplitAt -1"/>
<xsl:with-param name="pDelim" select="$pDelim"/>
</xsl:call-template>
</xsl:otherwise>
</xsl:choose>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
when this transformation is applied on the same XML document, the wanted, correct result is produced again:
<t>
<t1>FirstName&LastName&Address1&Address2&</t1>
<t2>City&State&country&</t2>
</t>