How can I convert all heading text to Title Case with XSLT? - xslt

I have a XML file containing a list of headings that I need to change to title case (words should begin with a capital letter except for most articles, conjunctions, and prepositions) with the help of XSLT.
Example:
"<h1>PERSONS OF THE DIALOGUE</h1>
in to
"<h1>Persons of the Dialogue</h1>
Please help...
Thanks

Here's a way you could do this:
<?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"
exclude-result-prefixes="xs"
version="2.0">
<xsl:output method="xml" indent="yes"/>
<xsl:variable name="words" select="'*a*,*an*,*the*,*and*,*but*,*for*,*nor*,*or*,*so*,*yet*,*as*,*at*,*by*,*if*,*in*,*of*,*on*,*to*,*with*,*when*,*where*'"/>
<xsl:template match="/">
<result>
<xsl:apply-templates select="document/h1"/>
</result>
</xsl:template>
<xsl:template match="h1">
<xsl:copy>
<xsl:variable name="elements" select="tokenize(lower-case(.), ' ')"/>
<xsl:for-each select="$elements">
<xsl:choose>
<!-- The first letter of the first word of a title is always Uppercase -->
<xsl:when test="position()=1">
<xsl:value-of select="upper-case(substring(.,1,1))"/>
<xsl:value-of select="substring(.,2)"/>
<xsl:if test="position()!=last()">
<xsl:text> </xsl:text>
</xsl:if>
</xsl:when>
<xsl:otherwise>
<xsl:choose>
<!-- If the word is contained in $words, leave it Lowercase -->
<xsl:when test="contains($words,concat('*',.,'*'))">
<xsl:value-of select="."/>
<xsl:if test="position()!=last()">
<xsl:text> </xsl:text>
</xsl:if>
</xsl:when>
<!-- If not, first letter is Uppercase -->
<xsl:otherwise>
<xsl:value-of select="upper-case(substring(.,1,1))"/>
<xsl:value-of select="substring(.,2)"/>
<xsl:if test="position()!=last()">
<xsl:text> </xsl:text>
</xsl:if>
</xsl:otherwise>
</xsl:choose>
</xsl:otherwise>
</xsl:choose>
</xsl:for-each>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
See it working here: https://xsltfiddle.liberty-development.net/93dFK9m/1

Related

XSLT using regex pattern

I have a requirements to get the value based on a priority #schemeNames. Get the value of ID if the #schemeName='TaxNumber' is present, else if #schemeName='PassportNumber', else if #schemeName is no value. After getting the value, it needs to check or ignore the 1st 2 characters if it is Alpha. Also, I need to consider the spaces between words in #schemeName. If for example, the value of my #schemeName is 'Tax Number' or 'taxnumber' it is valid. But if the value is like this, 't axNum Ber', it should not validate this value.
Here is my XSLT:
<xsl:stylesheet version="2.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="/">
<Result>
<xsl:for-each select="/Record/Data/ID">
<xsl:choose>
<xsl:when test="matches(lower-case(.[#schemeName]),'^tax\s+number')">
<xsl:if test="matches(substring(.,1,2),'^[a-zA-Z]+$')">
<xsl:value-of select="substring(.,3)"/>
</xsl:if>
</xsl:when>
<xsl:when test="matches(lower-case(.[#schemeName]),'^passport\s+number')">
<xsl:if test="matches(substring(.,1,2),'^[a-zA-Z]+$')">
<xsl:value-of select="substring(.,3)"/>
</xsl:if>
</xsl:when>
<xsl:when test=".[#schemeName='']">
<xsl:if test="matches(substring(.,1,2),'^[a-zA-Z]+$')">
<xsl:value-of select="substring(.,3)"/>
</xsl:if>
</xsl:when>
</xsl:choose>
</xsl:for-each>
</Result>
</xsl:template>
</xsl:stylesheet>
INPUT:
<Record>
<Data>
<ID schemeName="TaxNumber">PT123457</ID>
<ID schemeName="PassportNumber">PT098732</ID>
<ID schemeName="LicenseNumber">PT445423</ID>
<ID schemeName="">PT7566435</ID>
</Data>
</Record>
GENERATED OUTPUT:
<Result>7566435</Result>
The output generated is coming from the #schemeName that is null. It should be coming from the TaxNumber since it is present. There's something wrong in my condition when checking the #schemeNames.
I am using XSLT v2.0. Thank you!
How about setting your priorities in variables? Something like:
<xsl:variable name="prio1" select="ID[matches(lower-case(#schemeName),'^tax\s?number')]"/>
<xsl:variable name="prio1_value">
<xsl:choose>
<xsl:when test="matches($prio1, '^[A-z]{2}.*$')">
<xsl:value-of select="substring($prio1,3)"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$prio1"/>
</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:variable name="prio2" select="ID[matches(lower-case(#schemeName),'^passport\s?number')]"/>
<xsl:variable name="prio2_value">
<xsl:choose>
<xsl:when test="matches($prio2, '^[A-z]{2}.*$')">
<xsl:value-of select="substring($prio2,3)"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$prio2"/>
</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:variable name="prio3" select="ID[#schemeName='']"/>
<xsl:variable name="prio3_value">
<xsl:choose>
<xsl:when test="matches($prio3, '^[A-z]{2}.*$')">
<xsl:value-of select="substring($prio3,3)"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$prio3"/>
</xsl:otherwise>
</xsl:choose>
</xsl:variable>
Note: I have changed \s+ to \s? to make the space optional.
Also, you can use the if-then-else construct in xslt 2.0. The final code is below:
<xsl:stylesheet version="2.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="/">
<xsl:apply-templates select="Record/Data"/>
</xsl:template>
<xsl:template match="Data">
<xsl:variable name="prio1" select="ID[matches(lower-case(#schemeName),'^tax\s?number')]"/>
<xsl:variable name="prio1_value">
<xsl:choose>
<xsl:when test="matches($prio1, '^[A-z]{2}.*$')">
<xsl:value-of select="substring($prio1,3)"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$prio1"/>
</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:variable name="prio2" select="ID[matches(lower-case(#schemeName),'^passport\s?number')]"/>
<xsl:variable name="prio2_value">
<xsl:choose>
<xsl:when test="matches($prio2, '^[A-z]{2}.*$')">
<xsl:value-of select="substring($prio2,3)"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$prio2"/>
</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:variable name="prio3" select="ID[#schemeName='']"/>
<xsl:variable name="prio3_value">
<xsl:choose>
<xsl:when test="matches($prio3, '^[A-z]{2}.*$')">
<xsl:value-of select="substring($prio3,3)"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$prio3"/>
</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<Result>
<xsl:value-of select="if ($prio1) then ($prio1_value) else
if ($prio2) then ($prio2_value) else
if ($prio3) then ($prio3_value) else 0"/>
</Result>
</xsl:template>
</xsl:stylesheet>

Removing the root element using xslt

I have an xml schema that has a structure like
<Level>
<Level1>...data...</Level1>
<Level2>...data...</Level2>
.
.
.
</Level>
I want to remove the <Level> tag. How am I supposed to do that, with the help of xslt.
The standard answer to any "how do I keep most of my XML the same but tweak some little bits of it" question is "use an identity template and then override it for the specific things you want to change"
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<!-- omit the <?xml?> line in the output, it won't be well-formed anyway -->
<xsl:output method="xml" omit-xml-declaration="yes" />
<xsl:template match="#*|node()">
<xsl:copy><xsl:apply-templates select="#*|node()" /></xsl:copy>
</xsl:template>
<xsl:template match="/*">
<xsl:apply-templates select="node()" />
</xsl:template>
</xsl:stylesheet>
but as Mr Lister points out in the comments, this will leave you with something that is not well formed XML, as it will have more than one document element.
When I apply this stylesheet on the input XML
<Level>
<Level1>...data...</Level1>
<Level2>...data...</Level2>
</Level>
it produces the result
<Level1>...data...</Level1>
<Level2>...data...</Level2>
If you want to store all child elements of the document element in a variable, you can do something like:
<xsl:variable name="myVar" select="/*/*"/>
If, however, you want to the stylesheet to produce a string, this might be a solution:
<?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:template match="/*">
<xsl:apply-templates select="node()"/>
</xsl:template>
<xsl:template match="*">
<!-- We write the opening tag -->
<xsl:value-of select="concat('<',local-name())"/>
<!-- Now, all attributes -->
<xsl:apply-templates select="#*"/>
<!-- Depending on whether we have an empty element or not,
we're adding the content or closing it immediately -->
<xsl:choose>
<xsl:when test="node()">
<!-- Close opening tag -->
<xsl:value-of select="'>'"/>
<!-- Add the content -->
<xsl:apply-templates select="node()"/>
<!-- Write closing tag -->
<xsl:value-of select="concat('</',local-name(),'>')"/>
</xsl:when>
<xsl:otherwise>
<!-- Create empty element by closing tag immediately -->
<xsl:value-of select="'/>'"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template match="#*">
<!-- Write an attribute -->
<xsl:value-of select="concat(' ',local-name(),'="',.,'"')"/>
</xsl:template>
</xsl:stylesheet>
It produces text (and therefore you won't get non-well-formed XML). It's a little over-simplified because it does not handle namespaces, comments, processing instructions and quotes in attributes. If your input XML contains any of these, you'd have to refine the stylesheet.
I have created a new XSLT definition which fulfill my requremtn with the help of your code
<?xml version="1.0" encoding="UTF-8"?><xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output encoding="utf-8" method="text" omit-xml-declaration="yes"/>
<xsl:variable name="nl">
<xsl:text/>
</xsl:variable>
<xsl:variable name="tb">
<xsl:text/>
</xsl:variable>
<xsl:template match="/*">
<!-- Open the root array -->
<xsl:text>[{</xsl:text>
<xsl:value-of select="$nl"/>
<!-- Process all the child nodes of the root -->
<xsl:apply-templates mode="detect" select="*">
<xsl:with-param name="indent" select="$tb"/>
</xsl:apply-templates>
<!-- Close the root array -->
<xsl:value-of select="$nl"/>
<xsl:text>}]</xsl:text>
</xsl:template>
<xsl:template match="*" mode="detect">
<xsl:choose>
<xsl:when test="name(preceding-sibling::*[1]) = name(current()) and name(following-sibling::*[1]) != name(current())">
<xsl:apply-templates mode="obj-content" select="."/>
<xsl:text>]</xsl:text>
<xsl:if test="count(following-sibling::*[name() != name(current())]) > 0">, </xsl:if>
</xsl:when>
<xsl:when test="name(preceding-sibling::*[1]) = name(current())">
<xsl:apply-templates mode="obj-content" select="."/>
<xsl:if test="name(following-sibling::*) = name(current())">, </xsl:if>
</xsl:when>
<xsl:when test="following-sibling::*[1][name() = name(current())]">
<xsl:text>"</xsl:text>
<xsl:value-of select="name()"/>
<xsl:text>" : [</xsl:text>
<xsl:apply-templates mode="obj-content" select="."/>
<xsl:text>, </xsl:text>
</xsl:when>
<xsl:when test="count(./child::*) > 0 or count(#*) > 0">
<xsl:text>"</xsl:text>
<xsl:value-of select="name()"/>" : [<xsl:apply-templates
mode="obj-content" select="."/>
<xsl:if test="count(following-sibling::*) > 0">], </xsl:if>
</xsl:when>
<xsl:when test="count(./child::*) = 0">
<xsl:text>"</xsl:text>
<xsl:value-of select="name()"/>" : "<xsl:apply-templates select="."/>
<xsl:text>"</xsl:text>
<xsl:if test="count(following-sibling::*) > 0">, </xsl:if>
</xsl:when>
</xsl:choose>
</xsl:template>
<xsl:template match="*" mode="obj-content">
<xsl:text>{</xsl:text>
<xsl:apply-templates mode="attr" select="#*"/>
<xsl:if test="count(#*) > 0 and (count(child::*) > 0 or text())">, </xsl:if>
<xsl:apply-templates mode="detect" select="./*"/>
<xsl:if test="count(child::*) = 0 and text() and not(#*)">
<xsl:text>"</xsl:text>
<xsl:value-of select="name()"/>" : "<xsl:value-of select="text()"/>
<xsl:text>"</xsl:text>
</xsl:if>
<xsl:if test="count(child::*) = 0 and text() and #*">
<xsl:text>: "</xsl:text>
<xsl:value-of select="text()"/>
<xsl:text>"</xsl:text>
</xsl:if>
<xsl:text>}</xsl:text>
<xsl:if test="position() < last()">, </xsl:if>
</xsl:template>
<xsl:template match="#*" mode="attr">
<xsl:text>"</xsl:text>
<xsl:value-of select="name()"/>" : "<xsl:value-of select="."/>
<xsl:text>"</xsl:text>
<xsl:if test="position() < last()">,</xsl:if>
</xsl:template>
<xsl:template match="node/#TEXT | text()" name="removeBreaks">
<xsl:param name="pText" select="normalize-space(.)"/>
<xsl:choose>
<xsl:when test="not(contains($pText, '
'))">
<xsl:copy-of select="$pText"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="concat(substring-before($pText, '
'), ' ')"/>
<xsl:call-template name="removeBreaks">
<xsl:with-param name="pText" select="substring-after($pText, '
')"/>
</xsl:call-template>
</xsl:otherwise>
</xsl:choose>
</xsl:template>

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>

How to replace commas with spaces in XSL

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>

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>