tesmc
asked on
XSL: loop through string to obtain values then sum it
I have the following input XML.
<root>
<Envelope>
<Body>
<Response>
<Itin>
<Services Item="005"">
<Service Code="SOS">
<Extensions>
<Type>PAYMENT</Type>
</Extensions>
<Text>RA UK1 YVRYYZ0702P01DEC/SEAT/ACCP T/CAD1000/ 095CD/050A B CCVIX0001E 123</Text>
</Service>
</Services>
<Itin>
</Response>
</Body>
</Envelope>
</root>
I want to get the Text value and obtain the values after the 4th instance of "/" and before the next empty space. and build the following XML node
<Taxes Total="145">
<Tax Amount="95">
<Code>CD</Code>
</Tax>
<Tax Amount="50">
<Code>AB</Code>
</Tax>
</Taxes>
Where Total is sum of both Tax/@Amount.
Also, it is possible that Text would only contain only one tax code such as:
<Text>RA UK1 YVRYYZ0702P01DEC/SEAT/ACCP T/USD1500/ 075AB CCVIXX1237E 123</Text>
In which case,
<Taxes Total="75">
<Tax Amount="75">
<Code>AB</Code>
</Tax>
</Taxes>
<root>
<Envelope>
<Body>
<Response>
<Itin>
<Services Item="005"">
<Service Code="SOS">
<Extensions>
<Type>PAYMENT</Type>
</Extensions>
<Text>RA UK1 YVRYYZ0702P01DEC/SEAT/ACCP
</Service>
</Services>
<Itin>
</Response>
</Body>
</Envelope>
</root>
I want to get the Text value and obtain the values after the 4th instance of "/" and before the next empty space. and build the following XML node
<Taxes Total="145">
<Tax Amount="95">
<Code>CD</Code>
</Tax>
<Tax Amount="50">
<Code>AB</Code>
</Tax>
</Taxes>
Where Total is sum of both Tax/@Amount.
Also, it is possible that Text would only contain only one tax code such as:
<Text>RA UK1 YVRYYZ0702P01DEC/SEAT/ACCP
In which case,
<Taxes Total="75">
<Tax Amount="75">
<Code>AB</Code>
</Tax>
</Taxes>
ASKER CERTIFIED SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Honestly, tasks like this are a very good reason for moving to XSLT2
75% lines of code removed, code easier to grasp, no recursion and a huge performance difference
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:fun="#internal"
exclude-result-prefixes="fun"
version="2.0">
<xsl:output indent="yes"/>
<xsl:variable name="regex" select="'^(\d+)([^\d]+)$'"/>
<xsl:template match="/">
<xsl:apply-templates select="//Text"/>
</xsl:template>
<xsl:template match="Text">
<xsl:variable name="codes" select="substring-before(string-join(tokenize(normalize-space(.), '/')[position() > 4], '/'), ' ')"/>
<Taxes Total="{sum(for $i in tokenize($codes, '/') return number(fun:value-from-code($i)))}">
<xsl:for-each select="tokenize($codes, '/')">
<Tax Amount="{fun:value-from-code(.)}">
<Code>
<xsl:value-of select="fun:code-from-code(.)"/>
</Code>
</Tax>
</xsl:for-each>
</Taxes>
</xsl:template>
<xsl:function name="fun:value-from-code">
<xsl:param name="code"/>
<xsl:value-of select="number(replace($code, $regex, '$1'))"/>
</xsl:function>
<xsl:function name="fun:code-from-code">
<xsl:param name="code"/>
<xsl:value-of select="replace($code, $regex, '$2')"/>
</xsl:function>
</xsl:stylesheet>
both solutions tested and found working on both your examples
ASKER
Great. I will test on my end. Thx so much
ASKER
What is tokenize() and the purpose of xmlns:fun="#internal"
exclude-result-prefixes="f un"?
exclude-result-prefixes="f
tokenize() is an XPath2 function that breaks apart a string (argument 1) into a sequence on each match of a regular expression (argument 2)
tokenize('abcbdbe', 'b') will return a sequence ('a', 'c', 'd', 'e')
xmlns:fun="#internal"
functions in XSLT2 need to be in a namespace (the null namespace is reserved for XPath built in functions)
so I make a namespace
noone cares about what the uri for that one is,
in projects I use something like
urn:companyname:customerna me:project :xslt:func tions
eg.
xmlns:fun="urn:gertone:bad tz:stringl oop:xslt:f unctions"
I give the namespace a prefix, eg. "fun:"
and I make sure the namespace node does not make it to the output tree
exclude-result-prefixes="f un"
tokenize('abcbdbe', 'b') will return a sequence ('a', 'c', 'd', 'e')
xmlns:fun="#internal"
functions in XSLT2 need to be in a namespace (the null namespace is reserved for XPath built in functions)
so I make a namespace
noone cares about what the uri for that one is,
in projects I use something like
urn:companyname:customerna
eg.
xmlns:fun="urn:gertone:bad
I give the namespace a prefix, eg. "fun:"
and I make sure the namespace node does not make it to the output tree
exclude-result-prefixes="f
ASKER
I'm getting tokenize is an unknown xslt function.
OK, that means you are using an XSLT1 processor
I gave you an XSLT1 solution because your other XSLTs are XSLT1
The XSLT2 solution requires an XSLT2 processor such as saxon (www.saxonica.com)
I gave you an XSLT1 solution because your other XSLTs are XSLT1
The XSLT2 solution requires an XSLT2 processor such as saxon (www.saxonica.com)
ASKER
ok, that seemed to do the trick.
ASKER
thanks very much
welcome