XSLT using regex pattern - regex

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>

Related

How can I convert all heading text to Title Case with 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

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>

XSL Basics: showing value of a boolean?

When I have this in xsl:
<xsl:choose>
<xsl:when test="something > 0">
<xsl:variable name="myVar" select="true()"/>
</xsl:when>
<xsl:otherwise>
<xsl:variable name="myVar" select="false()"/>
</xsl:otherwise>
</xsl:choose>
How can I then print out the value of "myVar"? Or more importantly, how can I use this boolean in another choose statement?
<xsl:choose>
<xsl:when test="something > 0">
<xsl:variable name="myVar" select="true()"/>
</xsl:when>
<xsl:otherwise>
<xsl:variable name="myVar" select="false()"/>
</xsl:otherwise>
</xsl:choose>
This is quite wrong and useless, because the variable $myVar goes out of scope immediately.
One correct way to conditionally assign to the variable is:
<xsl:variable name="myVar">
<xsl:choose>
<xsl:when test="something > 0">1</xsl:when>
<xsl:otherwise>0</xsl:otherwise>
</xsl:choose>
</xsl:variable>
However, you really don't need this -- much simpler is:
<xsl:variable name="myVar" select="something > 0"/>
How can I then print out the value of "myVar"?
Use:
<xsl:value-of select="$myVar"/>
Or more importantly, how can I use this boolean in another choose
statement?
Here is a simple example:
<xsl:choose>
<xsl:when test="$myVar">
<!-- Do something -->
</xsl:when>
<xsl:otherwise>
<!-- Do something else -->
</xsl:otherwise>
</xsl:choose>
And here is a complete example:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:template match="/*/*">
<xsl:variable name="vNonNegative" select=". >= 0"/>
<xsl:value-of select="name()"/>: <xsl:text/>
<xsl:choose>
<xsl:when test="$vNonNegative">Above zero</xsl:when>
<xsl:otherwise>Below zero</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
when this transformation is applied on the following XML document:
<temps>
<Monday>-2</Monday>
<Tuesday>3</Tuesday>
</temps>
the wanted, correct result is produced:
Monday: Below zero
Tuesday: Above zero

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>