XSLT 1.0: string into array variable - xslt

I have a variable as below:
<xsl:variable name="ARRAY">
One,Two,Three,Four
</xsl:variable>
With XSLT 2.0 I used tokenize functions and I set an array variable:
<xsl:variable name="tokenizedSample" select="tokenize($ARRAY,',')"/>
and get array value with:
<xsl:value-of select="$tokenizedSample[1]"/>
Unfortunately I must use XSLT 1.0 and I don't know as replace this situation...
I found some examples to create a template as below:
<xsl:template name="SimpleStringLoop">
<xsl:param name="input"/>
<xsl:if test="string-length($input) > 0">
<xsl:variable name="v" select="substring-before($input, ',')"/>
<field>
<xsl:value-of select="$v"/>
</field>
<xsl:call-template name="SimpleStringLoop">
<xsl:with-param name="input" select="substring-after($input, ',')"/>
</xsl:call-template>
</xsl:if>
</xsl:template>
and calling this template as below:
<xsl:variable name="fields">
<xsl:call-template name="SimpleStringLoop">
<xsl:with-param name="input" select="$ARRAY"/>
</xsl:call-template>
</xsl:variable>
and accessing to this new array with:
<xsl:value-of select="$fields[1]"/>
but doesn't work.
How can I do?
I would like a XSLT 1.0 variable as array because I want read it with for example:
$newArray[1]
Thanks.

I don't see why you would define a variable that needs tokenizing, instead of defining it as "tokenized" to begin with.
In XSLT 1.0, this could be done as:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:my="http://www.example.com/my"
exclude-result-prefixes="my">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<my:items>
<item>One</item>
<item>Two</item>
<item>Three</item>
<item>Four</item>
</my:items>
<!-- the rest of the stylesheet -->
</xsl:stylesheet>
With this in place, you can do:
<xsl:value-of select="document('')/xsl:stylesheet/my:items/item[2]"/>
from anywhere in your stylesheet to retrieve "Two".
Of course, you could put the "array" into a variable:
<xsl:variable name="my-items" select="document('')/xsl:stylesheet/my:items/item" />
so that you can shorten the reference to:
<xsl:value-of select="$my-items[2]"/>

Related

How to pattern match in XSLT and add values

As part of an XSLT, I need to add all the values of the "Duration" element and display the value. Now, the below XML is a part of the larger XML I'm working on. In the below XML, I need to match
a/TimesheetDuration/Day*/Duration, add the values and display them. I dont want to store all the values in variables and add them. Is there any other clean way of doing this?
<?xml version="1.0" ?>
<a>
<TimesheetDuration>
<Day1>
<BusinessDate>6/12/2013</BusinessDate>
<Duration>03:00</Duration>
</Day1>
<Day2>
<BusinessDate>6/13/2013</BusinessDate>
<Duration>04:00</Duration>
</Day2>
<Day3>
<BusinessDate>6/14/2013</BusinessDate>
<Duration>05:00</Duration>
</Day3>
</TimesheetDuration>
</a>
An XPath 2.0 solution, assuming the durations are in the form HH:MM, would be
sum(for $d in a//Duration
return xs:dayTimeDuration(replace($d, '(..):(..)', 'PT$1H$2M')))
In xslt 1.0 you could do it for example with following stylesheet
<?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:template match="/">
<Durations>
<xsl:apply-templates select="a/TimesheetDuration/node()[starts-with(name(),'Day')]" />
<xsl:variable name="hours">
<xsl:call-template name="sumHours">
<xsl:with-param name="Day" select="a/TimesheetDuration/node()[starts-with(name(),'Day')][1]" />
</xsl:call-template>
</xsl:variable>
<SumOfHours>
<xsl:value-of select="$hours" />
</SumOfHours>
<!-- Sum of minutes would be calculated similarly -->
</Durations>
</xsl:template>
<xsl:template match="node()[starts-with(name(),'Day')]">
<xsl:copy-of select="Duration" />
</xsl:template>
<xsl:template name="sumHours">
<xsl:param name="tmpSum" select="0" />
<xsl:param name="Day" />
<xsl:variable name="newTmpSum" select="$tmpSum + substring-before($Day/Duration, ':')" />
<xsl:choose>
<xsl:when test="$Day/following-sibling::node()[starts-with(name(),'Day')]">
<xsl:call-template name="sumHours">
<xsl:with-param name="tmpSum" select="$newTmpSum" />
<xsl:with-param name="Day" select="$Day/following-sibling::node()[starts-with(name(),'Day')]" />
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$newTmpSum" />
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
It produces output
<?xml version="1.0" encoding="UTF-8"?>
<Durations>
<Duration>03:00</Duration>
<Duration>04:00</Duration>
<Duration>01:00</Duration>
<SumOfHours>8</SumOfHours>
</Durations>

How I can parse Pipeline sign from string in xslt

How I can parse pipeline sign from fields in xslt. e.g.
dummy1|dummy2|dummy3|dummy4
Regards,
Sarah
If you use an XSLT 2.0 processor you can use the tokenize function (http://www.w3.org/TR/xpath-functions/#func-tokenize) e.g. with an input of
<foo>dummy1|dummy2|dummy3|dummy4</foo>
you can match
<xsl:template match="foo">
<xsl:value-of select="tokenize(., '\|')"/>
</xsl:template>
to output dummy1 dummy2 dummy3 dummy4. If you use an XSLT 1.0 processor you can check whether it supports an extension function like http://www.exslt.org/str/functions/tokenize/ or you need to write a recursive, named template splitting up the input.
In xlst 1.0 I usually use recursive call of named template, e.g.
<?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="dummyVar" select="'dummy1|dummy2|dummy3|dummy4'" />
<xsl:variable name="delimiter" select="'|'" />
<xsl:template match="/">
<dummies>
<xsl:call-template name="parseDummy">
<xsl:with-param name="parsedString" select="$dummyVar" />
</xsl:call-template>
</dummies>
</xsl:template>
<xsl:template name="parseDummy">
<xsl:param name="parsedString" />
<xsl:choose>
<xsl:when test="contains($parsedString, $delimiter)">
<xsl:element name="{substring-before($parsedString, $delimiter)}" />
<xsl:call-template name="parseDummy">
<xsl:with-param name="parsedString" select="substring-after($parsedString, $delimiter)" />
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:element name="{$parsedString}" />
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
It's on you what you need to do with parsed values, in example I construct elements with names of dummies.
Be careful about context which is changing.

Using substring in xsl

Following on from an earlier question, and this is more about xsl syntax. I want to split part of a URL variable into a new variable in xsl.
This code works when the variable is sitting part way along a URL. EG:
http://www.mysite.com/test.aspx?aVar=something&bVar=somethingMore&cVar=yetMoreStill
<xsl:variable name="testVar" select="substring-after($url, 'bVar=')"/>
<xsl:value-of select="substring-before($testVar, '&')" />
The problem is the variable can sometime sit at the end of the URL (I have no control over this) EG:
http://www.mysite.com/test.aspx?aVar=something&bVar=somethingMore
So the above code fails. Is there away I can allow for both occurrences? The end game is I'm just trying to get the value of bVar no matter where it sits within the URL. Thanks.
How about the following workaround?
<xsl:variable name="testVar" select="substring-after($url, 'bVar=')"/>
<xsl:value-of select="substring-before(concat($testVar, '&'), '&')" />
Try this. This is XSLT 1.0:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:fo="http://www.w3.org/1999/XSL/Format" xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xsl:template match="/">
<xsl:call-template name="urlResolver">
<xsl:with-param name="input" select="'http://www.mysite.com/test.aspx?aVar=something&bVar=somethingMore'" />
</xsl:call-template>
<xsl:call-template name="urlResolver">
<xsl:with-param name="input" select="'http://www.mysite.com/test.aspx?aVar=something&bVar=somethingMore&cVar=yetMoreStill'" />
</xsl:call-template>
</xsl:template>
<xsl:template name="urlResolver">
<xsl:param name="input" />
<xsl:variable name="testVar" select="substring-after($input, 'bVar=')"/>
<xsl:choose>
<xsl:when test="contains($testVar, '&')"><xsl:value-of select="substring-before($testVar, '&')" /></xsl:when>
<xsl:otherwise><xsl:value-of select="$testVar" /></xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
Try to make use of tokenize (available in XSLT 2.0) like the following:
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output indent="yes" omit-xml-declaration="yes" method="xml" version="1.0"/>
<xsl:template match="/">
<xsl:variable name="test"><![CDATA[http://www.mysite.com/test.aspx?aVar=something&bVar=somethingMore&cVar=yetMoreStill]]></xsl:variable>
<xsl:variable name="splitURL" select="tokenize($test,'&')"/>
<xsl:variable name="bvar" select="$splitURL[starts-with(.,'bVar')]"/>
<out><xsl:value-of select="substring-after($bvar, 'bVar=')"/></out>
</xsl:template>
</xsl:stylesheet>
The currently accepted answer is generally wrong.
Try it with this URL:
http://www.mysite.com/test.aspx?subVar=something&bVar=somethingMore
and you get the wrong result: something
This question was already answered... In case you read the answer you would just reuse it and get your QString from the produced result:
<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:strip-space elements="*"/>
<xsl:param name="pUrl" select=
"'http://www.mysite.com/test.aspx?subVar=something&bVar=somethingMore'"/>
<xsl:template match="/">
<xsl:variable name="vrtfQStrings">
<xsl:call-template name="GetQueryStringParams"/>
</xsl:variable>
bVar = "<xsl:value-of select="ext:node-set($vrtfQStrings)/bVar"/>"
</xsl:template>
<xsl:template name="GetQueryStringParams">
<xsl:param name="pUrl" select="$pUrl"/>
<xsl:variable name="vQueryPart" select=
"substring-before(substring-after(concat($pUrl,'?'),
'?'),
'?')"/>
<xsl:variable name="vHeadVar" select=
"substring-before(concat($vQueryPart,'&'), '&')"/>
<xsl:element name="{substring-before($vHeadVar, '=')}">
<xsl:value-of select="substring-after($vHeadVar, '=')"/>
</xsl:element>
<xsl:variable name="vRest" select="substring-after($vQueryPart, '&')"/>
<xsl:if test="string-length($vRest) > 0">
<xsl:call-template name="GetQueryStringParams">
<xsl:with-param name="pUrl" select=
"concat('?', substring(substring-after($vQueryPart, $vHeadVar), 2))"/>
</xsl:call-template>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
When applied on any XML document (not used), this transformation produces the wanted, correct result:
bVar = "somethingMore"

how to get required name from a string in xslt?

e.g i have following strings:
xoc.coe.hw.ZSBALAJI
hw.cor.exp.nt.ZSSHIVA
i have to get only last string (i.e. ZSBALAJI from first and ZSSHIVA from second). How can I do it in xslt.
Thanks in advance.
Here is an XSLT-1.0 solution to your problem:
<?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"/>
<xsl:template match="//string">
<xsl:call-template name="skipper">
<xsl:with-param name="source" select="."/>
<xsl:with-param name="delimiter" select="'.'"/>
</xsl:call-template>
</xsl:template>
<!-- returns the substring after the last delimiter -->
<xsl:template name="skipper">
<xsl:param name="source"/>
<xsl:param name="delimiter"/>
<xsl:choose>
<xsl:when test="contains($source,$delimiter)">
<xsl:call-template name="skipper">
<xsl:with-param name="source" select="substring-after($source,$delimiter)"/>
<xsl:with-param name="delimiter" select="$delimiter"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$source"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
When applied to this document:
<?xml version="1.0" encoding="UTF-8"?>
<strings>
<string>xoc.coe.hw.ZSBALAJI</string>
<string>hw.cor.exp.nt.ZSSHIVA</string>
</strings>
It produces the following result:
ZSBALAJI
ZSSHIVA
Let's assume that you have the following XML:
<?xml version="1.0" encoding="UTF-8"?>
<root>
<a>xoc.coe.hw.ZSBALAJI</a>
<a>hw.cor.exp.nt.ZSSHIVA</a>
</root>
Then the following XSLT
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="2.0">
<xsl:output method="text"/>
<xsl:template match="//a">
<xsl:variable name="parts" select="tokenize(node(), '\.')"/>
<xsl:variable name="count" select="count($parts)"/>
<xsl:for-each select="$parts">
<xsl:if test="position() = $count">
<xsl:value-of select="."/>
</xsl:if>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
will ouput
ZSBALAJI
ZSSHIVA
Essentially, you can use XPath tokenize function and then take the last token.
You can try and use EXSLT tokenize(string, string?) function to split by '.' on the relevant node, see this for additional info.

XPath 1.0 max function on variables using EXSLT

I am serching an XPath function that works like the XPath 2.0 fn:max function. A function that returns the maximum of several parameters.
After searching a lot I figured out this way:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:math="http://exslt.org/math"
xmlns:exslt="http://exslt.org/common"
xmlns:func="http://exslt.org/functions"
xmlns:my="http://myns.com"
extension-element-prefixes="math exslt func">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:template match="/">
<root>
<xsl:value-of select="my:max(1,2)"/>
</root>
</xsl:template>
<func:function name="my:max">
<xsl:param name="e1"/>
<xsl:param name="e2"/>
<xsl:variable name="x">
<val><xsl:value-of select="$e1"/></val>
<val><xsl:value-of select="$e2"/></val>
</xsl:variable>
<func:result select="math:max(exslt:node-set($x)/val)"/>
</func:function>
</xsl:stylesheet>
Is it possible to do it so that my max function can take more elements?
Cheers
Jan
I don't have my XSLT 1.0 book in front of me, but I think the key here is that you can select 'node sets' and set those equal to your parameter variable, rather than having one-node-per-parameter.
Here's a rough guess:
<xsl:template match="/">
<root>
<xsl:call-template name="max">
<xsl:with-param name="values">
<val>1</val>
<val>2</val>
<val>3</val>
</xsl:with-param>
</xsl:call-template>
</root>
</xsl:template>
<func:function name="my:max">
<xsl:param name="x"/>
<func:result select="math:max($x/val/*)"/>
</func:function>
edit: re-read the question along with some XSLT 1.0 guidance. It should resemble the other answer, simplified only slightly. Keep in mind that if the numbers you want come from the XML data, you can use the select= attribute on xsl:with-param to automatically select the nodes you want to compare.
Presumably you could specify the xml (for the node-set) as the input argument?
I'm not "up" on exslt, but using msxsl (just for the node-set function, which is also in exslt):
<xsl:template name="max">
<xsl:param name="values"/>
<xsl:for-each select="msxsl:node-set($values)/val">
<xsl:sort data-type="number" order="descending"/>
<xsl:if test="position()=1">
<xsl:value-of select="."/>
</xsl:if>
</xsl:for-each>
</xsl:template>
...
<xsl:call-template name="max">
<xsl:with-param name="values">
<val>13</val>
<val>123</val>
<val>18</val>
</xsl:with-param>
</xsl:call-template>
Thank you for your ideas.
You helped me to understand everything a bit better. But my original intention was to get a
handy XPath function that works on xsl variables.
<!-- works with XPath 2.0 -->
<xst:template match="img/#height">
<xsl:variable name="$maximageheight" select="200">
<xsl:value-of select="fn:max( $maximageheight , . )"/>
</xsl:template>
<!-- until now the only way I see to do the same in XSL 1.0 -->
<xst:template match="img/#height">
<xsl:variable name="$maximageheight" select="200">
<xsl:call-template name="max">
<xsl:with-param name="values">
<val>$maximageheight</val>
<val><xsl:value-of select="."/></val>
</xsl:with-param>
</xsl:call-template>
</xsl:template>
For a fixed umber of parameters it would be possible to implement an exslt function:
<func:function name="my:max" xmlns:func="http://exslt.org/functions">
<xsl:param name="e1"/>
<xsl:param name="e2"/>
<xsl:variable name="x">
<val><xsl:value-of select="$e1"/></val>
<val><xsl:value-of select="$e2"/></val>
</xsl:variable>
<func:result select="math:max(exslt:node-set($x)/val)"/>
</func:function>
But i do not see a way to implement it vor a variable number of parameters.