Quetion about the xsl:template returning a xsl:map - xslt-3.0

I know that "Cannot add a map to an XML tree", especially take the xsl:map as result document.
But if I assign a xsl:template to a variable, it can take a xsl:map as result of the xsl:template. For example:
<?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:map="http://www.w3.org/2005/xpath-functions/map"
exclude-result-prefixes="xs"
version="3.0">
<xsl:template match="/">
<xsl:variable name="m1" as="map(*)">
<xsl:call-template name="tmap1"/>
</xsl:variable>
<xsl:variable name="m2" as="map(*)">
<xsl:call-template name="tmap2"/>
</xsl:variable>
</xsl:template>
<xsl:template name="tmap1">
<xsl:sequence select="map{1:'abc', 2:'ccd'}"/>
</xsl:template>
<xsl:template name="tmap2" as="map(*)">
<xsl:map>
<xsl:map-entry key="'Mo'" select="'Monday'"/>
<xsl:map-entry key="'Tu'" select="'Tuesday'"/>
<xsl:map-entry key="'We'" select="'Wednesday'"/>
<xsl:map-entry key="'Th'" select="'Thursday'"/>
<xsl:map-entry key="'Fr'" select="'Friday'"/>
<xsl:map-entry key="'Sa'" select="'Saturday'"/>
<xsl:map-entry key="'Su'" select="'Sunday'"/>
<xsl:map-entry key="'z2'" select="'day'"/>
</xsl:map>
</xsl:template>
These will run well. But I has an error with my work code, and I make a minimum but complete demo to reproduce it.
the code:
<?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:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main"
xmlns:rel="http://schemas.openxmlformats.org/package/2006/relationships"
xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships"
xmlns:v="urn:schemas-microsoft-com:vml"
xmlns:map = "http://www.w3.org/2005/xpath-functions/map"
exclude-result-prefixes="xs w rel r map v"
version="3.0">
<xsl:template match="/">
<xsl:apply-templates select="w:document/w:body/w:p"/>
</xsl:template>
<xsl:template match="w:p">
<xsl:element name="p">
<xsl:variable name="p_css" as="map(*)*">
<xsl:if test="w:pPr">
<xsl:variable name="t">
<xsl:apply-templates select="w:pPr" mode="style_item"/>
</xsl:variable>
<xsl:if test="not($t instance of map(*))">
<xsl:message>error</xsl:message>
</xsl:if>
</xsl:if>
</xsl:variable>
<xsl:if test="not(empty($p_css))">
<xsl:where-populated>
<xsl:attribute name="style" select="$p_css?2"/>
</xsl:where-populated>
</xsl:if>
<xsl:apply-templates select="w:t"/>
</xsl:element>
</xsl:template>
<xsl:template match="w:pPr" mode="style_item" as="map(*)*">
<xsl:sequence select="map{1:'hi', 2:'hello', 3:'world'}"/>
</xsl:template>
</xsl:stylesheet>
the source document:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<w:document xmlns:o="urn:schemas-microsoft-com:office:office"
xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships"
xmlns:v="urn:schemas-microsoft-com:vml"
xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main"
xmlns:w10="urn:schemas-microsoft-com:office:word"
xmlns:wp="http://schemas.openxmlformats.org/drawingml/2006/wordprocessingDrawing"
xmlns:wps="http://schemas.microsoft.com/office/word/2010/wordprocessingShape"
xmlns:wpg="http://schemas.microsoft.com/office/word/2010/wordprocessingGroup"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:wp14="http://schemas.microsoft.com/office/word/2010/wordprocessingDrawing"
xmlns:w14="http://schemas.microsoft.com/office/word/2010/wordml" mc:Ignorable="w14 wp14">
<w:body>
<w:p w:rsidR="00F708CA" w:rsidRDefault="00F708CA" w:rsidP="006E4E72">
<w:pPr>
<w:outlineLvl w:val="0"/>
</w:pPr>
<w:r>
<w:rPr>
<w:rFonts w:hint="eastAsia"/>
</w:rPr>
<w:t>你好,大家好。</w:t>
</w:r>
<w:bookmarkStart w:id="0" w:name="_GoBack"/>
<w:bookmarkEnd w:id="0"/>
</w:p>
<w:p>
<w:pPr>
<w:pStyle w:val="Normal"/>
<w:rPr>
<w:b/>
<w:b/>
<w:bCs/>
<w:color w:val="C9211E"/>
</w:rPr>
</w:pPr>
<w:r>
<w:rPr>
<w:b/>
<w:bCs/>
<w:color w:val="C9211E"/>
</w:rPr>
<w:t xml:space="preserve">12345good day. </w:t>
</w:r>
</w:p>
<w:p>
<w:pPr>
<w:pStyle w:val="Normal"/>
<w:rPr>
<w:i/>
<w:i/>
<w:iCs/>
<w:color w:val="77BC65"/>
<w:sz w:val="28"/>
<w:szCs w:val="28"/>
<w:highlight w:val="yellow"/>
</w:rPr>
</w:pPr>
<w:r>
<w:rPr>
<w:i/>
<w:iCs/>
<w:color w:val="77BC65"/>
<w:sz w:val="28"/>
<w:szCs w:val="28"/>
<w:highlight w:val="yellow"/>
</w:rPr>
<w:t>64789hello world</w:t>
</w:r>
</w:p>
<w:sectPr>
<w:type w:val="nextPage"/>
<w:pgSz w:w="11906" w:h="16838"/>
<w:pgMar w:left="1134" w:right="1134" w:header="0" w:top="1134" w:footer="0"
w:bottom="1134" w:gutter="0"/>
<w:pgNumType w:fmt="decimal"/>
<w:formProt w:val="false"/>
<w:textDirection w:val="lrTb"/>
</w:sectPr>
</w:body>
</w:document>
The error displays "Cannot add a map to an XML tree" when runtime,
my xslt proccessor is saxon-he 9.8.0.12

If you want a result (principal or secondary) to show/serialize a map then use the output method adaptive (or json if the map represents JSON) for that result:
<xsl:output method="adaptive" indent="yes"/>
<xsl:template match="/">
<xsl:variable name="m1" as="map(*)">
<xsl:call-template name="tmap1"/>
</xsl:variable>
<xsl:variable name="m2" as="map(*)">
<xsl:call-template name="tmap2"/>
</xsl:variable>
<xsl:sequence select="$m1, $m2"/>
</xsl:template>
https://xsltfiddle.liberty-development.net/gWEamLu
<xsl:output method="json" indent="yes"/>
<xsl:template match="/">
<xsl:variable name="m1" as="map(*)">
<xsl:call-template name="tmap1"/>
</xsl:variable>
<xsl:variable name="m2" as="map(*)">
<xsl:call-template name="tmap2"/>
</xsl:variable>
<xsl:sequence select="[$m1, $m2]"/>
</xsl:template>
https://xsltfiddle.liberty-development.net/gWEamLu/1

My problem is that the '<xsl:variable name="t">' variable don't have a explicit type declaration.

Related

pattern for generating JSON output

I'm trying to map XML to JSON using XSLT 3.0
my broad plan is to take the input, map it to some elements in memory, and then map that to 'map's and 'array's to by applying templates and then letting the XSLT serialise that as JSON.
Here is my initial effort:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="xs"
xmlns:map="http://www.w3.org/2005/xpath-functions/map"
version="3.0">
<xsl:output method="json" encoding="UTF-8" indent="yes"/>
<xsl:template match="/">
<xsl:variable name="dsl" as="element()">
<epg lastBuildDate="10/4/2019 9:46:00 AM">
</epg>
</xsl:variable>
<xsl:variable name="output">
<xsl:apply-templates select="$dsl" mode="interpret"/>
</xsl:variable>
<xsl:sequence select="$output"/>
</xsl:template>
<xsl:template match="epg" mode="interpret">
<xsl:sequence select="map {
'lastBuildDate' : #lastBuildDate
}"/>
</xsl:template>
</xsl:stylesheet>
sadly I get
Cannot add a map to an XDM node tree
in the 'interpret' template.
The error occurs with
<xsl:variable name="output">
<xsl:apply-templates select="$dsl" mode="interpret"/>
</xsl:variable>
as that is an implicit use of
<xsl:variable name="output">
<xsl:document>
<xsl:apply-templates select="$dsl" mode="interpret"/>
</xsl:document>
</xsl:variable>
While maps can contain nodes as values, nodes can't contain maps.
For that simple example it should work to use
<xsl:variable name="output" as="item()*">
<xsl:apply-templates select="$dsl" mode="interpret"/>
</xsl:variable>
or even
<xsl:variable name="output" as="item()">
<xsl:apply-templates select="$dsl" mode="interpret"/>
</xsl:variable>
or
<xsl:variable name="output" as="map(*)">
<xsl:apply-templates select="$dsl" mode="interpret"/>
</xsl:variable>
this works, I'll mark the other answer as correct though there is a 2nd issue where you have to 'cast' the attribute to a string
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="xs"
xmlns:map="http://www.w3.org/2005/xpath-functions/map"
version="3.0">
<xsl:output method="json" encoding="UTF-8" indent="yes"/>
<xsl:template match="/">
<xsl:variable name="dsl" as="element()">
<epg lastBuildDate="10/4/2019 9:46:00 AM">
</epg>
</xsl:variable>
<xsl:variable name="output" as="map(*)*">
<xsl:apply-templates select="$dsl" mode="interpret"/>
</xsl:variable>
<xsl:sequence select="$output"/>
</xsl:template>
<xsl:template match="epg" as="map(*)" mode="interpret">
<xsl:sequence select="map {
'lastBuildDate' : #lastBuildDate => xs:string()
}"/>
</xsl:template>
</xsl:stylesheet>

xsltproc XML parser multi value

I'm using xsltproc to parse XML content into text content. Need to separate multiple values for one tag.
My XML:
<data xml:space="preserve" id="USER">
<c1>USER NAME</c1>
<c2>ADDRESS</c2>
<c3>DET</c3>
<c4>AILS</c4>
<c5>1001</c5>
<c5 a="2">2001</c5>
<c5 a="3">3001</c5>
<c5 a="4">401</c5>
<c5 a="5">5001</c5>
<c5 a="6">6001</c5>
<c6>1</c6>
<c7>20991231M0601</c7>
</data>
My xsl:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text"/>
<!-- write out comma separated file -->
<xsl:template match="data">
<xsl:value-of select="c1"/>
<xsl:value-of select="','"/>
<xsl:value-of select="c2"/>
<xsl:value-of select="','"/>
<xsl:value-of select="c3"/>
<xsl:value-of select="','"/>
<xsl:value-of select="c4"/>
<xsl:value-of select="','"/>
<xsl:apply-templates select="c5"/>
<xsl:value-of select="','"/>
<xsl:value-of select="c6"/>
<xsl:value-of select="','"/>
<xsl:value-of select="c7"/>
<xsl:value-of select="','"/>
<xsl:text>
</xsl:text>
</xsl:template>
</xsl:stylesheet>
Post execution,
My output comes like,
USER NAME,ADDRESS,DET,AILS,10012001300140150016001,1,20991231M0601,
But i wanted an output like
USER NAME,ADDRESS,DET,AILS,1001|2001|3001|401|5001|6001,1,20991231M0601,
With separator for the multi value.
How about:
XSLT 1.0
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text"/>
<xsl:template match="data">
<xsl:for-each select="c1|c2|c3|c4">
<xsl:value-of select="."/>
<xsl:text>,</xsl:text>
</xsl:for-each>
<xsl:for-each select="c5">
<xsl:value-of select="."/>
<xsl:if test="position()!=last()">|</xsl:if>
</xsl:for-each>
<xsl:text>,</xsl:text>
<xsl:value-of select="c6"/>
<xsl:text>,</xsl:text>
<xsl:value-of select="c7"/>
<xsl:text>
</xsl:text>
</xsl:template>
</xsl:stylesheet>

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

Key Match using accumulators in XSLT3

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.

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