Key Match using accumulators in XSLT3 - xslt-3.0

I am looking to use accumulators in my xslt3 below and process only unmatched keys and ignore others.
I want loop through each All_Time_Offs/Time_Off and if Time_Off_Key is present in the Payroll_Input/Input_Key -> Then do not process. Else process the record
<?xml version="1.0" encoding="UTF-8"?>
<Aggregated_TimeOffs>
<All_Payroll_Inputs>
<Payroll_Input>
<RefID>PAYROLL_INPUT-6-122898</RefID>
<Emp_ID>101058</Emp_ID>
<Earning>101D</Earning>
<Start_Date/>
<Adjustment>0</Adjustment>
<Hours>4</Hours>
<Input_Key>PAYROLL_INPUT-6-122898101D4</Input_Key>
</Payroll_Input>
<Payroll_Input>
<RefID>PAYROLL_INPUT-6-122898</RefID>
<Emp_ID>101058</Emp_ID>
<Earning>101D</Earning>
<Start_Date/>
<Adjustment>0</Adjustment>
<Hours>4</Hours>
<Input_Key>PAYROLL_INPUT-6-122898101D9</Input_Key>
</Payroll_Input>
</All_Payroll_Inputs>
<All_Time_Offs>
<Time_Off>
<RefID/>
<Emp_ID>29519</Emp_ID>
<Earning/>
<Date>2020-09-10</Date>
<Adjustment/>
<Hours>7</Hours>
<Cost_Center/>
<Week>Week_2</Week>
<Time_Off_Key>PAYROLL_INPUT-6-122898101D4</Time_Off_Key>
</Time_Off>
<Time_Off>
<RefID/>
<Emp_ID>68413</Emp_ID>
<Earning/>
<Date>2020-09-09</Date>
<Adjustment/>
<Hours>8</Hours>
<Cost_Center/>
<Week>Week_2</Week>
<Time_Off_Key>INT024_PAYROLL_INPUT_2020-09-098</Time_Off_Key>
</Time_Off>
</All_Time_Offs>
</Aggregated_TimeOffs>
Below is the XSLT I am trying, but not working.
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:map="http://www.w3.org/2005/xpath-functions/map"
xmlns:err="http://www.w3.org/2005/xqt-errors"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:ext="urn:SomeExternalSource"
xmlns:xtt="urn:com.workday/xtt"
xmlns:wd="urn:com.workday/bsvc"
xmlns:this="urn:com.workday/this"
exclude-result-prefixes="xs ext map wd xtt this"
version="3.0">
<xsl:output method="xml" indent="yes" omit-xml-declaration="yes"></xsl:output>
<xsl:mode streamable="yes" on-no-match="shallow-skip" use-accumulators="Time_Off_Key Payroll_Input_lookup emp.id"/>
<xsl:accumulator name="Payroll_Input_lookup" as="xs:string" initial-value="''" streamable="yes">
<xsl:accumulator-rule match="Input_Key/text()" select="."/>
</xsl:accumulator>
<xsl:accumulator name="Time_Off_Key" as="map(xs:string,xs:string)" initial-value="map{}" streamable="yes">
<xsl:accumulator-rule match="Time_Off_Key/text()" select="map:put($value, string(.), accumulator-before('Payroll_Input_lookup'))"/>
</xsl:accumulator>
<xsl:accumulator name="emp.id" streamable="yes" as="xs:string" initial-value="''">
<xsl:accumulator-rule match="Emp_ID/text()" select="."/>
</xsl:accumulator>
<xsl:template match="Aggregated_TimeOffs">
<xsl:for-each select="All_Time_Offs/Time_Off/copy-of()">
<xsl:variable name="input_exists">
<xsl:value-of select="accumulator-before('Time_Off_Key')(normalize-space(Time_Off_Key))"/>
</xsl:variable>
<xsl:if test="string-length($input_exists) < 0">
<xsl:copy-of select="."/>
</xsl:if>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>

If you only want to compare the single values Time_Off_Key occured during "earlier" parsing of the Payroll_Input/Input_Key values then a single, xs:string*, i.e. string sequence based accumulator should suffice:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="3.0"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="#all"
expand-text="yes">
<xsl:output method="xml" indent="yes" omit-xml-declaration="yes"/>
<xsl:mode streamable="yes" on-no-match="shallow-skip" use-accumulators="Payroll_Input_Input_Keys"/>
<xsl:accumulator name="Payroll_Input_Input_Keys" as="xs:string*" initial-value="()" streamable="yes">
<xsl:accumulator-rule match="Payroll_Input/Input_Key/text()" select="., $value"/>
</xsl:accumulator>
<xsl:template match="Time_Off[copy-of()[not(Time_Off_Key = accumulator-before('Payroll_Input_Input_Keys'))]]">
<xsl:copy-of select="."/>
</xsl:template>
</xsl:stylesheet>
It might not be streamable, however, as the use of copy-of() in the predicate of the pattern breaks the motion-less requirement of streamable patterns: so we need to write
<xsl:template match="Time_Off">
<xsl:copy-of select="copy-of()[not(Time_Off_Key = accumulator-before('Payroll_Input_Input_Keys'))]"/>
</xsl:template>
instead to make that code work with streaming.

Related

extract and print values only when they exist using xslt

The source xml is:
<?xml version="1.0" encoding="UTF-8" ?>
<mr:collection
xmlns:mr="http://www.lc.gov/mr2/slim"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.lc.gov/mr2/slim http://www.lc.gov/standards/mrxml/schema/mr21slim.xsd">
<mr:rc>
<mr:ctr tg="1000">311</mr:ctr>
<mr:dtf tg="12000" i1="1" i2=" ">
<mr:sbf cd="d">John Diter</mr:sbf>
</mr:dtf>
</mr:rc>
</mr:collection>
the xsl that i use:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:d="http://www.example.com"
xpath-default-namespace="http://www.lc.gov/mr2/slim"
xmlns:mr="http://www.lc.gov/mr2/slim"
exclude-result-prefixes="#all"
expand-text="yes"
version="3.0">
<xsl:mode on-no-match="shallow-copy"/>
<xsl:output method="xml" indent="yes"/>
<xsl:template match="collection">
<O-PM>
<ListRcs>
<xsl:apply-templates/>
</ListRcs>
</O-PM>
</xsl:template>
<xsl:template match="rc">
<rc>
<xsl:apply-templates/>
</rc>
</xsl:template>
<xsl:template match="mr:ctr[#tg = 1000]">
<xsl:variable name="dz" as="xs:string">{tg ='1000'}!</xsl:variable><d:z xml:lang="en">{normalize-space($dz)}</d:z>
</xsl:template>
<xsl:template match="mr:dtf[#tg = 12000]">
<xsl:variable name="ds" as="xs:string">{sbf[#cd = 'a']}!</xsl:variable><d:s xml:lang="en">{normalize-space($ds)}</d:s>
<xsl:variable name="dp" as="xs:string">{sbf[#cd = 'c']}!</xsl:variable><d:p>{normalize-space($dp)}</d:p>
<xsl:variable name="dc" as="xs:string">{sbf[#cd = 'd']}!</xsl:variable><d:c>{normalize-space($dc)}</d:c>
</xsl:template>
</xsl:stylesheet>
Current output:
<?xml version="1.0" encoding="UTF-8"?>
<O-PM>
<ListRcs>
<rc>
<d:z xmlns:d="http://www.example.com">false!</d:z>
<d:s xmlns:d="http://www.example.com" xml:lang="en">!</d:s>
<d:p xmlns:d="http://www.example.com">!</d:p>
<d:c xmlns:d="http://www.example.com">John Diter!</d:c>
</rc>
</ListRcs>
</O-PM>
Desired output:
<?xml version="1.0" encoding="UTF-8"?>
<O-PM>
<ListRcs>
<rc>
<d:z xmlns:d="http://www.example.com">311</d:z>
<d:c xmlns:d="http://www.example.com">John Diter!</d:c>
</rc>
</ListRcs>
</O-PM>
For starters i need to get as output the value of tg = 1000 and not false,
and secondly, how can one print only the values that exist?
In the above example only the value that matches the criterion cd = d is TRUE.
https://xsltfiddle.liberty-development.net/asoTKA/2
It seems, this time you can just match on the elements that interest you:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:d="http://www.example.com"
xpath-default-namespace="http://www.lc.gov/mr2/slim"
xmlns:mr="http://www.lc.gov/mr2/slim"
exclude-result-prefixes="#all"
expand-text="yes"
version="3.0">
<xsl:mode on-no-match="shallow-skip"/>
<xsl:output method="xml" indent="yes"/>
<xsl:template match="collection">
<O-PM>
<ListRcs>
<xsl:apply-templates/>
</ListRcs>
</O-PM>
</xsl:template>
<xsl:template match="rc">
<rc>
<xsl:apply-templates/>
</rc>
</xsl:template>
<xsl:template match="mr:ctr[#tg = 1000]">
<d:z xml:lang="en">{.}</d:z>
</xsl:template>
<xsl:template match="dtf[#tg = 12000]/sbf[#cd = 'd']">
<d:c>{.}!</d:c>
</xsl:template>
</xsl:stylesheet>
https://xsltfiddle.liberty-development.net/asoTKA/3
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:d="http://www.example.com"
xpath-default-namespace="http://www.lc.gov/mr2/slim"
xmlns:mr="http://www.lc.gov/mr2/slim"
exclude-result-prefixes="#all"
expand-text="yes"
version="3.0">
<xsl:mode on-no-match="shallow-copy"/>
<xsl:output method="xml" indent="yes"/>
<xsl:template match="collection">
<O-PM>
<ListRcs>
<xsl:apply-templates/>
</ListRcs>
</O-PM>
</xsl:template>
<xsl:template match="rc">
<rc>
<xsl:apply-templates/>
</rc>
</xsl:template>
<xsl:template match="mr:ctr[#tg = 1000]">
<xsl:variable name="dz" as="xs:string"><xsl:value-of select="."/></xsl:variable>
<d:z xml:lang="en"><xsl:value-of select="normalize-space($dz)"/></d:z>
</xsl:template>
<xsl:template match="mr:dtf[#tg = 12000]">
<xsl:variable name="dc" as="xs:string">
<xsl:if test="mr:sbf[#cd = 'd']"><xsl:value-of select="normalize-space(.)"/> </xsl:if>
</xsl:variable>
<d:c xml:lang="en"><xsl:value-of select="normalize-space($dc)"/></d:c>
</xsl:template>
</xsl:stylesheet>
https://xsltfiddle.liberty-development.net/asoTKA/5

How can I handle error Exception with try/catch using xslt 3.0

How can I handle error Exception with try/catch using xslt 3.0. I am finding in xml number Element. If number Element not find in parent product then generate a file error.txt and how to write the Exception.
Input XML
<?xml version="1.0" encoding="UTF-8"?>
<catalog>
<product dept="WMN">
<name language="en">Fleece Pullover</name>
<colorChoices>navy black</colorChoices>
</product>
<product dept="ACC">
<number>563</number>
<name language="en">Floppy Sun Hat</name>
</product>
<product dept="ACC">
<number>443</number>
<name language="en">Deluxe Travel Bag</name>
</product>
<product dept="MEN">
<number>784</number>
<name language="en">Cotton Dress Shirt</name>
<colorChoices>white gray</colorChoices>
<desc>Our <i>favorite</i> shirt!</desc>
</product>
</catalog>
Using XSLT
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:err="http://www.w3.org/2005/xqt-errors"
exclude-result-prefixes="xs"
version="3.0">
<xsl:template match="/">
<xsl:stream href="books.xml">
<xsl:iterate select="catalog">
<xsl:result-document href="out.xml" omit-xml-declaration="no" indent="yes">
<xsl:copy>
<xsl:for-each select="product">
<xsl:try>
<xsl:copy-of select="number"/>
<xsl:catch>
<xsl:result-document href="error.txt">
<xsl:message>Element number not given in <xsl:value-of select="product/#dept"/></xsl:message>
<error code="{$err:code}" message="{$err:description}"/>
</xsl:result-document>
</xsl:catch>
</xsl:try>
</xsl:for-each>
</xsl:copy>
</xsl:result-document>
</xsl:iterate>
</xsl:stream>
</xsl:template>
</xsl:stylesheet>
My requirement is how can I uses try/catch when not find the number element then make a file with error.
Try this to generate error message in log file by taking the value in variable in then check some conditon:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:err="http://www.w3.org/2005/xqt-errors"
exclude-result-prefixes="xs"
version="3.0">
<xsl:template match="/">
<xsl:stream href="books.xml">
<xsl:iterate select="catalog">
<xsl:result-document href="out.xml" omit-xml-declaration="no" indent="yes">
<xsl:copy>
<xsl:for-each select="product">
<xsl:variable name="num" select="if (number) then (number) else ('aaaa')"/>
<xsl:try>
<number>
<xsl:value-of select="format-number($num, '############')"/>
</number>
<xsl:catch>
<xsl:result-document href="error.log" omit-xml-declaration="yes">
<xsl:value-of select="concat(' Element number not given in ', #dept)"/>
<xsl:value-of select="concat(' Error code: ', $err:code, ' and Error Desc is: ', $err:description)"/>
</xsl:result-document>
</xsl:catch>
</xsl:try>
</xsl:for-each>
</xsl:copy>
</xsl:result-document>
</xsl:iterate>
</xsl:stream>
</xsl:template>
</xsl:stylesheet>

Print xml nodes for each element in a single row

I need to print the category and hours values from all nodes in this xml as comma separated values in a single row through xslt -
XML
<?xml version="1.0" encoding="UTF-8"?>
<course>
<subcourse>
<code>ABC</code>
<name>REFCOURSE</name>
<date>Date</date>
<category>SDF</category>
<hours>7</hours>
</subcourse>
<subcourse>
<code>DEF</code>
<name>ORIGCOURSE</name>
<date>Date</date>
<category>UIT</category>
<hours>9</hours>
</subcourse>
</course>
Output needed -
SDF,7,UIT,9
By taking help from stakoverflow, here's what I've done so far -
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xs="http://www.w3.org/2001/XMLSchema" version="2.0">
<xsl:param name="range-1-begin" select="4"/>
<xsl:param name="range-1-end" select="5"/>
<xsl:param name="range-2-begin" select="6"/>
<xsl:param name="range-2-end" select="7"/>
<xsl:output method="text" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="subcourse">
<info><xsl:apply-templates/></info>
</xsl:template>
<xsl:template match="subcourse">
<xsl:if test = "not(position()= 1)">
<xsl:text>,</xsl:text>
</xsl:if>
<xsl:value-of select="."/>
</xsl:template>
</xsl:stylesheet>
Output - ABCREFCOURSEDateSDF7,DEFORIGCOURSEDateUIT9
I need it to iterate through every subcourse and pick category and hours if exist. I could not find how to pick only category and hours.
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xs="http://www.w3.org/2001/XMLSchema" version="2.0">
<xsl:template match="//subcource">
<xsl:if test="category">
<xsl:if test = "not(position()=1)">
<xsl:text>,</xsl:text>
</xsl:if>
<xsl:value-of select="category"/><xsl:text>,</xsl:text><xsl:value-of select="hours"/>
</xsl:if>
</xsl:template>
</xsl:stylesheet>

XSLT 2.0 : Split a string into comma separated values

I am new to XSLT and have a requirement where in i have to manipulate a string as below.
Input string = "12345"
Output expected ="12345,1234,123,12"
Can anybody help me to achieve this in XSLT 2.0
Here is some XSLT/XPath 2.0 approach:
<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:mf="http://example.com/mf"
exclude-result-prefixes="xs mf"
version="2.0">
<xsl:function name="mf:sub-sequences" as="xs:string*">
<xsl:param name="input" as="xs:string"/>
<xsl:param name="min-length" as="xs:integer"/>
<xsl:sequence select="reverse(
for $length in $min-length to string-length($input)
return substring($input, 1, $length)
)"/>
</xsl:function>
<xsl:template name="main">
<xsl:variable name="s" select="'12345'"/>
<xsl:value-of select="mf:sub-sequences($s, 2)" separator=","/>
</xsl:template>
</xsl:stylesheet>
This should do the trick:
<xsl:template match="/">
<xsl:call-template name="minus-one">
<xsl:with-param name="input" select="'12345'"/>
</xsl:call-template>
</xsl:template>
<xsl:template name="minus-one">
<xsl:param name="input"/>
<xsl:value-of select="$input"/>
<xsl:if test="string-length($input) gt 2"><xsl:text>,</xsl:text>
<xsl:call-template name="minus-one">
<xsl:with-param name="input" select="substring($input, 1, string-length($input) - 1)"/>
</xsl:call-template>
</xsl:if>
</xsl:template>
Here is a more efficient solution than the currently accepted one that doesn't use the reverse() function:
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:my="my:my">
<xsl:output method="text"/>
<xsl:function name="my:subSequences" as="xs:string*">
<xsl:param name="pString" as="xs:string"/>
<xsl:param name="pstartLength" as="xs:integer"/>
<xsl:sequence select=
"for $totalLength in string-length($pString),
$length in 1 to $totalLength -$pstartLength +1,
$revLength in $totalLength -$length +1
return
substring($pString, 1, $revLength)"/>
</xsl:function>
<xsl:template match="/">
<xsl:value-of select="my:subSequences('12345', 2)" separator=","/>
</xsl:template>
</xsl:stylesheet>
When this transformation is executed, the wanted, correct result is produced:
12345,1234,123,12
Explanation:
The XPath 2.0 W3C Spec defines that if the first argument of the to operator is greater than the second argument, then the resulting sequence is the empty sequense.
It is still possible to avoid this limitation and to construct a decreasing integer sequence, like this:
for $k in 0 to $big - $small
return
$big - $k
Using such expression is more efficient, especially for large sequences, than first constructing an increasing sequence and then reversing it with the reverse() function.
on
<string>12345</string>
the following xslt will produce the result 12345,1234,123,12
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:msxsl="urn:schemas-microsoft-com:xslt" exclude-result-prefixes="msxsl">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="string">
<xsl:call-template name="doTheFunkeyMonkey">
<xsl:with-param name="data" select="."/>
</xsl:call-template>
</xsl:template>
<xsl:template name="doTheFunkeyMonkey">
<xsl:param name="data"/>
<xsl:value-of select="$data"/>
<xsl:if test="string-length($data) > 2">
<xsl:text>,</xsl:text>
<xsl:call-template name="doTheFunkeyMonkey">
<xsl:with-param name="data" select="substring($data,1,string-length($data)-1)"/>
</xsl:call-template>
</xsl:if>
</xsl:template>
</xsl:stylesheet>

how to handle array of strings in ".xsl" file?

I have an array of strings in .xsl file, now I have to use the each string separeted by spaces differently. How I can get the strings?
I have following array of strings:
strarray="hw.oh.xml hg.hd.gnl th.ik.lkj"
I have to get "hw.oh.xml" , "hg.hd.gnl" , "th.ik.lkj" strings separetaly to perform some operation on it.
How I can do that?
There are many ways to do this:
I. Using the XPath substring-before() and substring-after() functions:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:variable name="vStrArray" select="'hw.oh.xml hg.hd.gnl th.ik.lkj'"/>
<xsl:template match="/">
<xsl:value-of select="substring-before($vStrArray, ' ')"/>
<xsl:text>
</xsl:text>
<xsl:value-of select="substring-before(substring-after($vStrArray, ' '),' ')"/>
<xsl:text>
</xsl:text>
<xsl:value-of select="substring-after(substring-after($vStrArray, ' '),' ')"/>
<xsl:text>
</xsl:text>
</xsl:template>
</xsl:stylesheet>
when this transformation is applied on any XML document (not used), the wanted result (each item from the "array") is produced:
hw.oh.xml
hg.hd.gnl
th.ik.lkj
This method can quickly become overwhelmingly complex and is not recommended except for "arrays" of just 2-3 items.
II. Representing the "array" as an XML document in XSLT 1.0:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:my="my:my">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<my:array>
<item>hw.oh.xml</item>
<item>hg.hd.gnl</item>
<item>th.ik.lkj</item>
</my:array>
<xsl:variable name="vStrArray"
select="document('')/*/my:array/*"/>
<xsl:template match="/">
<xsl:value-of select="$vStrArray[1]"/>
<xsl:text>
</xsl:text>
<xsl:value-of select="$vStrArray[2]"/>
<xsl:text>
</xsl:text>
<xsl:value-of select="$vStrArray[3]"/>
<xsl:text>
</xsl:text>
</xsl:template>
</xsl:stylesheet>
when this transformation is applied on the same XML document (any), the wanted correct result is produced:
hw.oh.xml
hg.hd.gnl
th.ik.lkj
I recommend this method of representing an "array" -- for XSLT 1.0 applications.
III. XSLT 2.0 / XPath 2.0
Simply use a sequence of strings:
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:variable name="vStrArray"
select="'hw.oh.xml', 'hg.hd.gnl', 'th.ik.lkj'"/>
<xsl:template match="/">
<xsl:sequence select=
"for $i in 1 to count($vStrArray)
return
concat($vStrArray[$i], '
')
"/>
</xsl:template>
</xsl:stylesheet>
Result:
hw.oh.xml
hg.hd.gnl
th.ik.lkj
UPDATE: The OP commented that he is stuck with the initial representation of space-separated values, contained in a single string.
IV. Convert the space-separated values string into an XML fragment for easy use.
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:ext="http://exslt.org/common">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:variable name="vStrArray" select="'hw.oh.xml hg.hd.gnl th.ik.lkj'"/>
<xsl:variable name="vrtfDoc">
<xsl:call-template name="makeIndex">
<xsl:with-param name="pText" select="$vStrArray"/>
</xsl:call-template>
</xsl:variable>
<xsl:variable name="vStrIndex" select="ext:node-set($vrtfDoc)/*"/>
<xsl:template match="/">
<xsl:value-of select="$vStrIndex[1]"/>
<xsl:text>
</xsl:text>
<xsl:value-of select="$vStrIndex[2]"/>
<xsl:text>
</xsl:text>
<xsl:value-of select="$vStrIndex[3]"/>
</xsl:template>
<xsl:template name="makeIndex">
<xsl:param name="pText"/>
<xsl:if test="string-length($pText)>0">
<item>
<xsl:value-of select=
"substring-before(concat($pText,' '), ' ')"/>
</item>
<xsl:call-template name="makeIndex">
<xsl:with-param name="pText" select=
"substring-after($pText,' ')"/>
</xsl:call-template>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
This transformation creates an XML fragment from the string in which every <item> element contains just one of the string values. Then its use is just as if it were an array.

Resources