XSLT - Looping through an array of dates - xslt

I need to know all weekends from two dates. I actually got a template which returns the day number (0-6) when passing a date as parameter, however, the problem is i need to loop through the dates between the two dates and calculate just business days.
Is there a way to store all these dates in an array and loop through that array?
Do i need to use C# embedded?
I'm using XSLT V 1.0 with SharePoint Designer.
Thanks.

This is not exactly trivial to do in pure XSLT 1.0.
Weekend dates in a given range:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:param name="startDate" select="'2014-05-01'"/>
<xsl:param name="endDate" select="'2014-05-31'"/>
<xsl:template match="/">
<weekends>
<xsl:call-template name="weekends">
<xsl:with-param name="startJDN">
<xsl:call-template name="JDN">
<xsl:with-param name="date" select="$startDate" />
</xsl:call-template>
</xsl:with-param>
<xsl:with-param name="endJDN">
<xsl:call-template name="JDN">
<xsl:with-param name="date" select="$endDate" />
</xsl:call-template>
</xsl:with-param>
</xsl:call-template>
</weekends>
</xsl:template>
<xsl:template name="weekends">
<xsl:param name="startJDN"/>
<xsl:param name="endJDN"/>
<xsl:if test="$startJDN mod 7 > 4">
<date>
<xsl:call-template name="GD">
<xsl:with-param name="JDN" select="$startJDN" />
</xsl:call-template>
</date>
</xsl:if>
<xsl:if test="$startJDN < $endJDN">
<xsl:call-template name="weekends">
<xsl:with-param name="startJDN" select="$startJDN + 1"/>
<xsl:with-param name="endJDN" select="$endJDN"/>
</xsl:call-template>
</xsl:if>
</xsl:template>
<xsl:template name="JDN">
<xsl:param name="date"/>
<xsl:param name="year" select="substring($date, 1, 4)"/>
<xsl:param name="month" select="substring($date, 6, 2)"/>
<xsl:param name="day" select="substring($date, 9, 2)"/>
<xsl:param name="a" select="floor((14 - $month) div 12)"/>
<xsl:param name="y" select="$year + 4800 - $a"/>
<xsl:param name="m" select="$month + 12*$a - 3"/>
<xsl:value-of select="$day + floor((153*$m + 2) div 5) + 365*$y + floor($y div 4) - floor($y div 100) + floor($y div 400) - 32045" />
</xsl:template>
<xsl:template name="GD">
<xsl:param name="JDN"/>
<xsl:param name="f" select="$JDN + 1401 + floor((floor((4 * $JDN + 274277) div 146097) * 3) div 4) - 38"/>
<xsl:param name="e" select="4*$f + 3"/>
<xsl:param name="g" select="floor(($e mod 1461) div 4)"/>
<xsl:param name="h" select="5*$g + 2"/>
<xsl:param name="D" select="floor(($h mod 153) div 5 ) + 1"/>
<xsl:param name="M" select="(floor($h div 153) + 2) mod 12 + 1"/>
<xsl:param name="Y" select="floor($e div 1461) - 4716 + floor((14 - $M) div 12)"/>
<xsl:value-of select="concat($Y, '-', format-number($M, '00'), '-', format-number($D, '00'))" />
</xsl:template>
</xsl:stylesheet>
The result of the above is a list of all Saturdays and Sundays in the month of May 2014:
<?xml version="1.0" encoding="UTF-8"?>
<weekends>
<date>2014-05-03</date>
<date>2014-05-04</date>
<date>2014-05-10</date>
<date>2014-05-11</date>
<date>2014-05-17</date>
<date>2014-05-18</date>
<date>2014-05-24</date>
<date>2014-05-25</date>
<date>2014-05-31</date>
</weekends>

Related

XSLT 1.0 Date Duration (Years)

I'm using XSLT 1.0 to output the duration between two date fields. The following code (from https://stackoverflow.com/a/38615456/3016153) outputs the Duration in Days, Hours, Minutes, and Seconds as expected, but I need to update it to output Years as well. Does anyone have any suggestions on how I can update this code to output the Year duration? Alternative code is welcomed as well, but it must use XSLT 1.0
Any assistance is greatly appreciated.
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="event">
<xsl:variable name="start">
<xsl:call-template name="dateTime-to-seconds">
<xsl:with-param name="dateTime" select="start/#time" />
</xsl:call-template>
</xsl:variable>
<xsl:variable name="end">
<xsl:call-template name="dateTime-to-seconds">
<xsl:with-param name="dateTime" select="end/#time" />
</xsl:call-template>
</xsl:variable>
<xsl:variable name="duration" select="$end - $start" />
<xsl:variable name="d" select="floor($duration div 86400)"/>
<xsl:variable name="t" select="$duration mod 86400"/>
<xsl:variable name="h" select="floor($t div 3600)"/>
<xsl:variable name="r" select="$t mod 3600"/>
<xsl:variable name="m" select="floor($r div 60)"/>
<xsl:variable name="s" select="$r mod 60"/>
<xsl:copy>
<xsl:copy-of select="name"/>
<duration>
<xsl:value-of select="$d"/>
<xsl:text> days, </xsl:text>
<xsl:value-of select="$h"/>
<xsl:text> hours, </xsl:text>
<xsl:value-of select="$m"/>
<xsl:text> minutes and </xsl:text>
<xsl:value-of select="$s"/>
<xsl:text> seconds</xsl:text>
</duration>
</xsl:copy>
</xsl:template>
<xsl:template name="dateTime-to-seconds">
<xsl:param name="dateTime"/>
<xsl:variable name="date" select="substring-before($dateTime, 'T')" />
<xsl:variable name="time" select="substring-after($dateTime, 'T')" />
<xsl:variable name="local-time" select="substring($time, 1, string-length($time) - 6)" />
<xsl:variable name="offset" select="substring-after($time, $local-time)" />
<xsl:variable name="year" select="substring($date, 1, 4)" />
<xsl:variable name="month" select="substring($date, 6, 2)" />
<xsl:variable name="day" select="substring($date, 9, 2)" />
<xsl:variable name="hour" select="substring($local-time, 1, 2)" />
<xsl:variable name="minute" select="substring($local-time, 4, 2)" />
<xsl:variable name="second" select="substring($local-time, 7)" />
<xsl:variable name="offset-sign" select="1 - 2 * starts-with($offset, '-')" />
<xsl:variable name="offset-hour" select="substring($offset, 2, 2) * $offset-sign" />
<xsl:variable name="offset-minute" select="substring($offset, 5, 2) * $offset-sign" />
<xsl:variable name="a" select="floor((14 - $month) div 12)"/>
<xsl:variable name="y" select="$year + 4800 - $a"/>
<xsl:variable name="m" select="$month + 12*$a - 3"/>
<xsl:variable name="jd" select="$day + floor((153*$m + 2) div 5) + 365*$y + floor($y div 4) - floor($y div 100) + floor($y div 400) - 32045" />
<xsl:value-of select="86400*$jd + 3600*$hour + 60*$minute + $second - 3600*$offset-hour - 60*$offset-minute" />
</xsl:template>
</xsl:stylesheet>
Sample XML:
<events>
<event>
<name>Alpha</name>
<start time="2015-02-21T00:59:06+02:00"/>
<end time="2018-02-22T02:24:38+02:00"/>
</event>
<event>
<name>Bravo</name>
<start time="2016-02-21T00:59:06+02:00"/>
<end time="2020-03-05T02:24:38+02:00"/>
</event>
<event>
<name>Charlie</name>
<start time="2016-02-21T00:59:06+02:00"/>
<end time="2020-02-21T00:59:06+01:00"/>
</event>
enter code here
As stated in the comments, if you are willing to assume that every 365 days constitute a year, then all you need to do is change this part:
<duration>
<xsl:value-of select="$d"/>
<xsl:text> days, </xsl:text>
<xsl:value-of select="$h"/>
<xsl:text> hours, </xsl:text>
<xsl:value-of select="$m"/>
<xsl:text> minutes and </xsl:text>
<xsl:value-of select="$s"/>
<xsl:text> seconds</xsl:text>
</duration>
to:
<duration>
<xsl:value-of select="floor($d div 365)"/>
<xsl:text> years, </xsl:text>
<xsl:value-of select="$d mod 365"/>
<xsl:text> days, </xsl:text>
<xsl:value-of select="$h"/>
<xsl:text> hours, </xsl:text>
<xsl:value-of select="$m"/>
<xsl:text> minutes and </xsl:text>
<xsl:value-of select="$s"/>
<xsl:text> seconds</xsl:text>
</duration>
As also stated in the comments, it is rather rather strange that you would be willing to accept an error of approximately 1 day per 4 years in a calculation that calculates to seconds precision. If you only need precision of days, then you should save processor time and electricity by removing the parts that calculate the hours, minutes and seconds.

XSLT : compare date timezone

i am new to XSLT
How to compare the date strings in XSLT and output the result
below is my input xml
<?xml version="1.0" encoding="UTF-8"?>
<dates>
<date1>2003-09-15T16:53:22.000-07:00</date1>
<date2>2003-09-15T16:53:23.000-07:00</date2>
</dates>
below is my XSLT
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:variable name="date1" select="/dates/date1"/>
<xsl:variable name="date2" select="/dates/date2"/>
<xsl:template match="/">
<xsl:if test="$date1 > $date2">
date1 is greater
</xsl:if>
<xsl:if test="$date1 = $date2">
both dates are equal
</xsl:if>
<xsl:if test="$date1 <= $date2">
date1 is lesser than date2
</xsl:if>
</xsl:template>
</xsl:stylesheet>
now in XSLT i want to compare the above dates, so is it possible in XSLT 1.0 to compare (greater,lesser, equals) the dates
i believe in xslt 1.0 its not possible, if possible please share the information.
if it can be done in XSLT 2.0 please help me how i can fix this,
in my current ongoing study project, i have used XSLT 1.0, so please suggest answer in XSLT 1.0 thanks
To do this in XSLT 1.0, you must first convert the given dateTimes to numerical values and equalize them to a common time zone.
In the following stylesheet, each dateTime is converted to the number of seconds elapsed since noon UTC of November 24, 4714 BC - see: https://en.wikipedia.org/wiki/Julian_day
XSLT 1.0
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="dates">
<xsl:variable name="date1">
<xsl:call-template name="dateTime-to-seconds">
<xsl:with-param name="dateTime" select="date1" />
</xsl:call-template>
</xsl:variable>
<xsl:variable name="date2">
<xsl:call-template name="dateTime-to-seconds">
<xsl:with-param name="dateTime" select="date2" />
</xsl:call-template>
</xsl:variable>
<result>
<xsl:choose>
<xsl:when test="$date1 < $date2">date1 occurs earlier than date2</xsl:when>
<xsl:when test="$date1 = $date2">the two dates are concurrent</xsl:when>
<xsl:when test="$date1 > $date2">date1 occurs later than date2</xsl:when>
</xsl:choose>
</result>
</xsl:template>
<xsl:template name="dateTime-to-seconds">
<xsl:param name="dateTime"/>
<xsl:variable name="date" select="substring-before($dateTime, 'T')" />
<xsl:variable name="time" select="substring-after($dateTime, 'T')" />
<xsl:variable name="local-time" select="substring($time, 1, string-length($time) - 6)" />
<xsl:variable name="offset" select="substring-after($time, $local-time)" />
<xsl:variable name="year" select="substring($date, 1, 4)" />
<xsl:variable name="month" select="substring($date, 6, 2)" />
<xsl:variable name="day" select="substring($date, 9, 2)" />
<xsl:variable name="hour" select="substring($local-time, 1, 2)" />
<xsl:variable name="minute" select="substring($local-time, 4, 2)" />
<xsl:variable name="second" select="substring($local-time, 7)" />
<xsl:variable name="offset-sign" select="1 - 2 * starts-with($offset, '-')" />
<xsl:variable name="offset-hour" select="substring($offset, 2, 2) * $offset-sign" />
<xsl:variable name="offset-minute" select="substring($offset, 5, 2) * $offset-sign" />
<xsl:variable name="a" select="floor((14 - $month) div 12)"/>
<xsl:variable name="y" select="$year + 4800 - $a"/>
<xsl:variable name="m" select="$month + 12*$a - 3"/>
<xsl:variable name="jd" select="$day + floor((153*$m + 2) div 5) + 365*$y + floor($y div 4) - floor($y div 100) + floor($y div 400) - 32045" />
<xsl:value-of select="86400*$jd + 3600*$hour + 60*$minute + $second - 3600*$offset-hour - 60*$offset-minute" />
</xsl:template>
</xsl:stylesheet>
Applied to the following test input:
XML
<dates>
<date1>2003-09-15T16:53:22.000-07:00</date1>
<date2>2003-09-15T17:53:22.000-06:00</date2>
</dates>
the result will be:
<?xml version="1.0" encoding="UTF-8"?>
<result>the two dates are concurrent</result>

How to do square root in xslt 1.0

I want to do some calculation within the xslt file and need to do some squre root in the formula. can someone please point out if it is possible and how please?
Thanks a mill
One can use the sqrt template/function of FXSL:
I. In XSLT 1.0:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
>
<xsl:import href="sqrt.xsl"/>
<!-- To be applied on any source xml.
This also tests the within() function
-->
<xsl:output indent="yes" omit-xml-declaration="yes"/>
<xsl:template match="/">
sqrt(0.25):
<xsl:call-template name="sqrt">
<xsl:with-param name="N" select="0.25"/>
<xsl:with-param name="Eps" select="0.00001"/>
</xsl:call-template>
sqrt(1):
<xsl:call-template name="sqrt">
<xsl:with-param name="N" select="1"/>
<xsl:with-param name="Eps" select="0.00001"/>
</xsl:call-template>
sqrt(2):
<xsl:call-template name="sqrt">
<xsl:with-param name="N" select="2"/>
<xsl:with-param name="Eps" select="0.00001"/>
</xsl:call-template>
sqrt(9):
<xsl:call-template name="sqrt">
<xsl:with-param name="N" select="9"/>
<xsl:with-param name="Eps" select="0.00001"/>
</xsl:call-template>
sqrt(16):
<xsl:call-template name="sqrt">
<xsl:with-param name="N" select="16"/>
<xsl:with-param name="Eps" select="0.00001"/>
</xsl:call-template>
sqrt(25):
<xsl:call-template name="sqrt">
<xsl:with-param name="N" select="25"/>
<xsl:with-param name="Eps" select="0.00001"/>
</xsl:call-template>
sqrt(36):
<xsl:call-template name="sqrt">
<xsl:with-param name="N" select="36"/>
<xsl:with-param name="Eps" select="0.00001"/>
</xsl:call-template>
sqrt(49):
<xsl:call-template name="sqrt">
<xsl:with-param name="N" select="49"/>
<xsl:with-param name="Eps" select="0.00001"/>
</xsl:call-template>
sqrt(64):
<xsl:call-template name="sqrt">
<xsl:with-param name="N" select="64"/>
<xsl:with-param name="Eps" select="0.00001"/>
</xsl:call-template>
sqrt(81):
<xsl:call-template name="sqrt">
<xsl:with-param name="N" select="81"/>
<xsl:with-param name="Eps" select="0.00001"/>
</xsl:call-template>
sqrt(100):
<xsl:call-template name="sqrt">
<xsl:with-param name="N" select="100"/>
<xsl:with-param name="Eps" select="0.00001"/>
</xsl:call-template>
sqrt(121):
<xsl:call-template name="sqrt">
<xsl:with-param name="N" select="121"/>
<xsl:with-param name="Eps" select="0.00001"/>
</xsl:call-template>
</xsl:template>
</xsl:stylesheet>
When this transformation is applied on any XML document (not used), the wanted correct result is produced:
sqrt(0.25):
0.5000000795866174
sqrt(1):
1.0000000464611474
sqrt(2):
1.4142156862745097
sqrt(9):
3.0000000000393214
sqrt(16):
4.0000001858445895
sqrt(25):
5.000000000016778
sqrt(36):
6.000000002793968
sqrt(49):
7.000000094930961
sqrt(64):
8.000001273385879
sqrt(81):
9.000009415515176
sqrt(100):
10.000000000107445
sqrt(121):
11.000000001323027
II. Using FXSL 2.x for XSLT 2.0:
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:f="http://fxsl.sf.net/">
<xsl:import href="../f/func-sqrt.xsl"/>
<xsl:import href="../f/func-map.xsl"/>
<xsl:import href="../f/func-standardXpathFunctions.xsl"/>
<xsl:output method="text"/>
<xsl:template match="/">
<xsl:value-of separator="
" select=
"f:map(f:round-half-to-even(f:sqrt(2, 0.000001)),
0 to 13
)
"/>
</xsl:template>
</xsl:stylesheet>
When this transformation is applied on any XML document (not used), the wanted, correct result is produced:
1
1.4
1.41
1.414
1.4142
1.41421
1.414214
1.4142136
1.41421356
1.414213562
1.4142135624
1.41421356237
1.414213562375
1.4142135623747
If you are using XSLT 2.0, then problem sorted -- See Dimitre's answer.
If you are using XSLT 1.0, in practice in a production environment, I would still go with Dimitre's answer. Commercially, it is better to use a tested libary than re-invent the wheel.
However I thought your question might be fun to implement a stand-alone solution for. And it was!
Given this bunch of values to find the roots of...
<squares>
<square>0</square>
<square>0.25</square>
<square>1</square>
<square>9</square>
<square>10</square>
</squares>
...when input to this XSLT 1.0 style-sheet...
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes"/>
<xsl:variable name="verysmall" select="0.00001" />
<xsl:template match="/">
<roots>
<xsl:apply-templates select="*/square"/>
</roots>
</xsl:template>
<xsl:template match="square">
<root>
<xsl:call-template name="root">
<xsl:with-param name="x" select="." />
</xsl:call-template>
</root>
</xsl:template>
<xsl:template name="root">
<xsl:param name="x"/><!-- Assume x >= 0 -->
<xsl:choose>
<xsl:when test="$x > 1">
<xsl:call-template name="iterate-root">
<xsl:with-param name="x" select="$x" />
<xsl:with-param name="H" select="$x" />
<xsl:with-param name="L" select="0" />
</xsl:call-template>
</xsl:when>
<xsl:when test="($x = 1) or ($x <= 0)">
<xsl:value-of select="$x" />
</xsl:when>
<xsl:otherwise>
<xsl:variable name="inv-root">
<xsl:call-template name="iterate-root">
<xsl:with-param name="x" select="1 div $x" />
<xsl:with-param name="H" select="1 div $x" />
<xsl:with-param name="L" select="0" />
</xsl:call-template>
</xsl:variable>
<xsl:value-of select="1 div $inv-root" />
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template name="iterate-root">
<xsl:param name="x"/> <!-- Assume x > 1 -->
<xsl:param name="H"/> <!-- High estimate -->
<xsl:param name="L"/> <!-- Low estimate -->
<xsl:variable name="M" select="($H + $L) div 2" />
<xsl:variable name="g" select="($M * $M - $x) div $x" />
<xsl:choose>
<xsl:when test="(($g < $verysmall) and ((- $g) < $verysmall)) or
(($H - $L) < $verysmall)">
<xsl:value-of select="$M"/>
</xsl:when>
<xsl:when test="$g > 0">
<xsl:call-template name="iterate-root">
<xsl:with-param name="x" select="$x" />
<xsl:with-param name="H" select="$M" />
<xsl:with-param name="L" select="$L" />
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:call-template name="iterate-root">
<xsl:with-param name="x" select="$x" />
<xsl:with-param name="H" select="$H" />
<xsl:with-param name="L" select="$M" />
</xsl:call-template>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
...yields approximate solutions...
<roots>
<root>0</root>
<root>0.5</root>
<root>1</root>
<root>2.999988555908203</root>
<root>3.1622695922851562</root>
</roots>
Notes
The technique uses Divide-And-Conquer to find the root of the equation f(y) = y*y - x, which occurs at the square root of x. The technique is probably only reasonably efficient for XSLT processors which implement optimized tail-end recursion.

Transpose XML using Muenchian method - multiple groupings

I need to transpose an XML file such as this:
<?xml version="1.0" encoding="UTF-8"?>
<products>
<product id="10" name="Widget 1" price="15.99" category_code="T" category="Toys" manufacturer_code="SC1" manufacturer="Some Company 1" />
<product id="10" name="Widget 1" price="15.99" category_code="E" category="Electronics" manufacturer_code="SC1" manufacturer="Some Company 1" />
<product id="10" name="Widget 1" price="15.99" category_code="T" category="Toys" manufacturer_code="SC2" manufacturer="Some Company 2" />
<product id="10" name="Widget 1" price="15.99" category_code="E" category="Electronics" manufacturer_code="SC2" manufacturer="Some Company 2" />
<product id="10" name="Widget 1" price="15.99" category_code="T" category="Toys" manufacturer_code="SC3" manufacturer="Some Company 3" />
<product id="10" name="Widget 1" price="15.99" category_code="E" category="Electronics" manufacturer_code="SC3" manufacturer="Some Company 3" />
<product id="11" name="Widget 2" price="21.99" category_code="V" category="Video Games" manufacturer_code="SC4" manufacturer="Some Company 4" />
<product id="12" name="Widget 3" price="10.99" category_code="T" category="Toys" manufacturer_code="SC1" manufacturer="Some Company 2" />
</products>
to a comma delimited text file, or a properly formated HTML table that includes only one line for each product, something like this:
id,name,price,category_code_1,category_1,category_code_2,category_2,manufacturer_code_1,manufacturer_1,manufacturer_code_2,manufacturer_2,manufacturer_code_3,manufacturer_3
10, Widget 1, 15.99, T, Toys, E, Electronics, SC1, Some Company 1, SC2, Some Company 2, SC3, Some Company 3
11, Widget 2, 21.99, V, Video Games,,, SC4, Some Company 4,,,,
12, Widget 3, 10.99, T, Toys,,, SC1, Some Company 2,,,,
As you'd notice, the XML data can be thought of as the result of three tables joined together: product, product_category, and product_manufacturer. Each product can belong to multiple categories and have multiple manufacturers. Of course the real data that I'm dealing with is more complex and in a totally different domain, but this sample depicts the problem properly.
I have very limited knowledge of XSLT, and with help of SO and other resources on the Interent, have put together a stylesheet that partly provides me what I need:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="html" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:key name="key_product_group" match="product" use="#id"/>
<xsl:key name="key_category_group" match="product" use="concat(
#id,
#category_code,
#category)"/>
<xsl:key name="key_manufacturer_group" match="product" use="concat(
#id,
#manufacturer_code,
#manufacturer)"/>
<xsl:variable name="var_max_category_group" >
<xsl:for-each select="//product[generate-id(.) = generate-id(key('key_product_group',#id) )]">
<xsl:sort select="count(key('key_product_group',#id )[generate-id() = generate-id(key('key_category_group',
concat(#id,
#category_code,
#category)))])" order="descending"/>
<xsl:if test="position() = 1">
<xsl:value-of select="count(key('key_product_group',#id )[generate-id() = generate-id(key('key_category_group',
concat(#id,
#category_code,
#category)))])"/>
</xsl:if>
</xsl:for-each>
</xsl:variable>
<xsl:variable name="var_max_manufacturer_group">
<xsl:for-each select="//product[generate-id(.) = generate-id(key('key_product_group',#id) )]">
<xsl:sort select="count(key('key_product_group',#id )[generate-id() = generate-id(key('key_manufacturer_group',
concat(#id,
#category_code,
#category)))])" order="descending"/>
<xsl:if test="position() = 1">
<xsl:value-of select="count(key('key_product_group',#id )[generate-id() = generate-id(key('key_manufacturer_group',
concat(#id,
#manufacturer_code,
#manufacturer)))])"/>
</xsl:if>
</xsl:for-each>
</xsl:variable>
<xsl:template match="/">
<xsl:text>id,</xsl:text>
<xsl:text>name,</xsl:text>
<xsl:text>price,</xsl:text>
<xsl:call-template name="loop_pcat">
<xsl:with-param name="count" select="$var_max_category_group"/>
</xsl:call-template>
<xsl:call-template name="loop_pmf">
<xsl:with-param name="count" select="$var_max_manufacturer_group"/>
</xsl:call-template>
<br></br>
<xsl:variable name="var_group"
select="//product[generate-id(.) = generate-id(key('key_product_group',#id)[1])]"/>
<xsl:for-each select="$var_group">
<xsl:sort order="ascending" select="#id"/>
<xsl:value-of select="#id"/>,
<xsl:value-of select="#name"/>,
<xsl:value-of select="#price"/>,
<xsl:for-each select="key('key_product_group',#id )[generate-id() = generate-id(key('key_category_group',
concat(#id,
#category_code,
#category)))]">
<xsl:value-of select="#category_code"/>,
<xsl:value-of select="#category"/>,
</xsl:for-each>
<xsl:for-each select="key('key_product_group',#id )[generate-id() = generate-id(key('key_manufacturer_group',
concat(#id,
#manufacturer_code,
#manufacturer)))]">
<xsl:value-of select="#manufacturer_code"/>,
<xsl:value-of select="#manufacturer"/>,
</xsl:for-each>
<br></br>
</xsl:for-each>
</xsl:template>
<xsl:template name="loop_pcat">
<xsl:param name="count" select="1"/>
<xsl:param name="limit" select="$count+1"/>
<xsl:if test="$count > 0">
<xsl:value-of select="concat('category_code_',$limit - $count)"/>
<xsl:text>,</xsl:text>
<xsl:value-of select="concat('category_',$limit - $count)"/>
<xsl:text>,</xsl:text>
<xsl:call-template name="loop_pcat">
<xsl:with-param name="count" select="$count - 1"/>
<xsl:with-param name="limit" select="$limit"/>
</xsl:call-template>
</xsl:if>
</xsl:template>
<xsl:template name="loop_pmf">
<xsl:param name="count" select="1"/>
<xsl:param name="limit" select="$count+1"/>
<xsl:if test="$count > 0">
<xsl:value-of select="concat('manufacturer_code_',$limit - $count)"/>
<xsl:text>,</xsl:text>
<xsl:value-of select="concat('manufacturer_',$limit - $count)"/>
<xsl:text>,</xsl:text>
<xsl:call-template name="loop_pmf">
<xsl:with-param name="count" select="$count - 1"/>
<xsl:with-param name="limit" select="$limit"/>
</xsl:call-template>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
The above stylesheet produces the following results:
id,name,price,category_code_1,category_1,category_code_2,category_2,manufacturer_code_1,manufacturer_1,manufacturer_code_2,manufacturer_2,manufacturer_code_3,manufacturer_3,
10, Widget 1, 15.99, T, Toys, E, Electronics, SC1, Some Company 1, SC2, Some Company 2, SC3, Some Company 3,
11, Widget 2, 21.99, V, Video Games, SC4, Some Company 4,
12, Widget 3, 10.99, T, Toys, SC1, Some Company 2,
The output has at least one major problem: all columns do not exist in each line, for example line 2 and 3 are missing category_code_2, and category_2, and manufacture_code and manufacturer 2 and 3. I'm sure there are other problems with the stylesheet as well, and I have no idea how it would perform on a relatively large xml file, but for now I would greatly appreciate your help with making the stylesheet produce the desired output format.
Thank you
MRSA
I had already begun with my own implementation, but then you posted and I was able to reuse some of your code to speed things up a bit. Main differences are:
in the templates, delimiters are added in front instead of after data; that way, you can end your lines properly.
the keys only use the ids and the codes (not the names).
the concatenation for the keys also uses a delimiter ('#').
the main template only provides the header line.
the product template has simpler calls to the data templates.
modified the data templates accordingly…
Here is my resulting stylesheet and its output:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text" version="1.0" encoding="UTF-8"/>
<xsl:key name="byProduct" match="product" use="#id"/>
<xsl:key name="byProductAndCategory" match="product" use="concat(#id,'#',#category_code)"/>
<xsl:key name="byProductAndManufacturer" match="product" use="concat(#id,'#',#manufacturer_code)"/>
<xsl:variable name="delimiter" select="','"/>
<xsl:variable name="eol" select="'
'"/>
<xsl:variable name="maxManufacturers">
<xsl:for-each select="//product[count(.|key('byProduct',#id)[1])=1]">
<xsl:sort select="count(key('byProductAndCategory',concat(#id,'#',#category_code)))"/>
<xsl:if test="position()=last()">
<xsl:value-of select="count(key('byProductAndCategory',concat(#id,'#',#category_code)))"/>
</xsl:if>
</xsl:for-each>
</xsl:variable>
<xsl:variable name="maxCategories">
<xsl:for-each select="//product[count(.|key('byProduct',#id)[1])=1]">
<xsl:sort select="count(key('byProductAndManufacturer',concat(#id,'#',#manufacturer_code)))"/>
<xsl:if test="position()=last()">
<xsl:value-of select="count(key('byProductAndManufacturer',concat(#id,'#',#manufacturer_code)))"/>
</xsl:if>
</xsl:for-each>
</xsl:variable>
<xsl:template match="/">
<!--create the file header-->
<xsl:text>id</xsl:text>
<xsl:value-of select="$delimiter"/>
<xsl:text>name</xsl:text>
<xsl:value-of select="$delimiter"/>
<xsl:text>price</xsl:text>
<!--recursive loop for each group to add appropriate number of columns, i.e. maximum number of repetitions-->
<xsl:call-template name="loop_pcat_header">
<xsl:with-param name="count" select="$maxCategories"/>
</xsl:call-template>
<!--recursive loop for each group to add appropriate number of columns, i.e. maximum number of repetitions-->
<xsl:call-template name="loop_pmf_header">
<xsl:with-param name="count" select="$maxManufacturers"/>
</xsl:call-template>
<xsl:value-of select="$eol"/>
<xsl:apply-templates select="//product[count(.|key('byProduct',#id)[1])=1]"/>
</xsl:template>
<xsl:template match="product">
<!-- id -->
<xsl:value-of select="#id"/>
<xsl:value-of select="$delimiter"/>
<!-- name -->
<xsl:value-of select="#name"/>
<xsl:value-of select="$delimiter"/>
<!-- price -->
<xsl:value-of select="#price"/>
<!-- category stuff -->
<xsl:call-template name="loop_pcat_data">
<xsl:with-param name="data" select="key('byProductAndManufacturer',concat(#id,'#',#manufacturer_code))"/>
<xsl:with-param name="count" select="$maxCategories"/>
</xsl:call-template>
<!-- manufacturer stuff -->
<xsl:call-template name="loop_pmf_data">
<xsl:with-param name="data" select="key('byProductAndCategory',concat(#id,'#',#category_code))"/>
<xsl:with-param name="count" select="$maxManufacturers"/>
</xsl:call-template>
<xsl:value-of select="$eol"/>
</xsl:template>
<!--recursive loop templates for file header and data-->
<xsl:template name="loop_pcat_header">
<xsl:param name="count" select="1"/>
<xsl:param name="limit" select="$count+1"/>
<xsl:if test="$count > 0">
<xsl:value-of select="$delimiter"/>
<xsl:value-of select="concat('category_code_',$limit - $count)"/>
<xsl:value-of select="$delimiter"/>
<xsl:value-of select="concat('category_',$limit - $count)"/>
<xsl:call-template name="loop_pcat_header">
<xsl:with-param name="count" select="$count - 1"/>
<xsl:with-param name="limit" select="$limit"/>
</xsl:call-template>
</xsl:if>
</xsl:template>
<xsl:template name="loop_pmf_header">
<xsl:param name="count" select="1"/>
<xsl:param name="limit" select="$count+1"/>
<xsl:if test="$count > 0">
<xsl:value-of select="$delimiter"/>
<xsl:value-of select="concat('manufacturer_code_',$limit - $count)"/>
<xsl:value-of select="$delimiter"/>
<xsl:value-of select="concat('manufacturer_',$limit - $count)"/>
<xsl:call-template name="loop_pmf_header">
<xsl:with-param name="count" select="$count - 1"/>
<xsl:with-param name="limit" select="$limit"/>
</xsl:call-template>
</xsl:if>
</xsl:template>
<xsl:template name="loop_pcat_data">
<xsl:param name="data"/>
<xsl:param name="count" select="1"/>
<xsl:param name="limit" select="$count+1"/>
<xsl:if test="$count > 0">
<xsl:value-of select="$delimiter"/>
<xsl:value-of select="$data[$limit - $count]/#category_code"/>
<xsl:value-of select="$delimiter"/>
<xsl:value-of select="$data[$limit - $count]/#category"/>
<xsl:call-template name="loop_pcat_data">
<xsl:with-param name="data" select="$data"/>
<xsl:with-param name="count" select="$count - 1"/>
<xsl:with-param name="limit" select="$limit"/>
</xsl:call-template>
</xsl:if>
</xsl:template>
<xsl:template name="loop_pmf_data">
<xsl:param name="data"/>
<xsl:param name="count" select="1"/>
<xsl:param name="limit" select="$count+1"/>
<xsl:if test="$count > 0">
<xsl:value-of select="$delimiter"/>
<xsl:value-of select="$data[$limit - $count]/#manufacturer_code"/>
<xsl:value-of select="$delimiter"/>
<xsl:value-of select="$data[$limit - $count]/#manufacturer"/>
<xsl:call-template name="loop_pmf_data">
<xsl:with-param name="data" select="$data"/>
<xsl:with-param name="count" select="$count - 1"/>
<xsl:with-param name="limit" select="$limit"/>
</xsl:call-template>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
The requested output:
id,name,price,category_code_1,category_1,category_code_2,category_2,manufacturer_code_1,manufacturer_1,manufacturer_code_2,manufacturer_2,manufacturer_code_3,manufacturer_3
10,Widget 1,15.99,T,Toys,E,Electronics,SC1,Some Company 1,SC2,Some Company 2,SC3,Some Company 3
11,Widget 2,21.99,V,Video Games,,,SC4,Some Company 4,,,,
12,Widget 3,10.99,T,Toys,,,SC1,Some Company 2,,,,
So this is what I came up with, I still have to take care of extra comma at the end, also deal with special characters such as single or double qoutes in the attribute values. Any help with optimizing the code is greatly appreciated.
The following stylesheet
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text" version="1.0" encoding="UTF-8" indent="yes"/>
<!--Generate keys, first is the product group-->
<xsl:key name="key_product_group" match="product" use="#id"/>
<!--Here is keys for groupings that are needed, note concatenation of attributes-->
<xsl:key name="key_category_group" match="product" use="concat(
#id,
#category_code,
#category)"/>
<!--another key-->
<xsl:key name="key_manufacturer_group" match="product" use="concat(
#id,
#manufacturer_code,
#manufacturer)"/>
<!--we need the maximum number of distinct groups for each product so that can format the layout properly-->
<xsl:variable name="var_max_category_group" >
<xsl:for-each select="//product[generate-id(.) = generate-id(key('key_product_group',#id) )]">
<xsl:sort select="count(key('key_product_group',#id )[generate-id() = generate-id(key('key_category_group',
concat(#id,
#category_code,
#category)))])" order="descending"/>
<xsl:if test="position() = 1">
<xsl:value-of select="count(key('key_product_group',#id )[generate-id() = generate-id(key('key_category_group',
concat(#id,
#category_code,
#category)))])"/>
</xsl:if>
</xsl:for-each>
</xsl:variable>
<!--maximum for the second group-->
<xsl:variable name="var_max_manufacturer_group">
<xsl:for-each select="//product[generate-id(.) = generate-id(key('key_product_group',#id) )]">
<xsl:sort select="count(key('key_product_group',#id )[generate-id() = generate-id(key('key_manufacturer_group',
concat(#id,
#category_code,
#category)))])" order="descending"/>
<xsl:if test="position() = 1">
<xsl:value-of select="count(key('key_product_group',#id )[generate-id() = generate-id(key('key_manufacturer_group',
concat(#id,
#manufacturer_code,
#manufacturer)))])"/>
</xsl:if>
</xsl:for-each>
</xsl:variable>
<!--delmiter and line break characters defined here-->
<xsl:variable name="var_delimiter" select="','"/>
<xsl:variable name="var_line_break" select="'
'"/>
<!--main procedure starts here-->
<xsl:template match="/">
<!--create the file header-->
<xsl:text>id</xsl:text>
<xsl:value-of select="$var_delimiter"/>
<xsl:text>name</xsl:text>
<xsl:value-of select="$var_delimiter"/>
<xsl:text>price</xsl:text>
<xsl:value-of select="$var_delimiter"/>
<!--recursive loop for each group to add appropriate number of columns, i.e. maximum number of repetitions-->
<xsl:call-template name="loop_pcat_header">
<xsl:with-param name="count" select="$var_max_category_group"/>
</xsl:call-template>
<!--recursive loop for each group to add appropriate number of columns, i.e. maximum number of repetitions-->
<xsl:call-template name="loop_pmf_header">
<xsl:with-param name="count" select="$var_max_manufacturer_group"/>
</xsl:call-template>
<xsl:value-of select="$var_line_break"/>
<!--select products and order ascending, the ordering is not really needed-->
<xsl:variable name="var_group"
select="//product[generate-id(.) = generate-id(key('key_product_group',#id)[1])]"/>
<xsl:for-each select="$var_group">
<xsl:sort order="ascending" select="#id"/>
<!--select non-repeatable attributes-->
<xsl:value-of select="#id"/>
<xsl:value-of select="$var_delimiter"/>
<xsl:value-of select="#name"/>
<xsl:value-of select="$var_delimiter"/>
<xsl:value-of select="#price"/>
<xsl:value-of select="$var_delimiter"/>
<!--select repeatable groups for each product-->
<xsl:for-each select="key('key_product_group',#id )[generate-id() = generate-id(key('key_category_group',
concat(#id,
#category_code,
#category)))]">
<xsl:value-of select="#category_code"/>
<xsl:value-of select="$var_delimiter"/>
<xsl:value-of select="#category"/>
<xsl:value-of select="$var_delimiter"/>
<!--count number of groups for each product and add blanks for the difference between the max and current instance-->
<xsl:variable name="var_max_category_group_instance" select="count(key('key_product_group',#id )[generate-id() = generate-id(key('key_category_group',
concat(#id,
#category_code,
#category)))])"/>
<xsl:call-template name="loop_pcat_data">
<xsl:with-param name="count" select="$var_max_category_group - $var_max_category_group_instance"/>
</xsl:call-template>
</xsl:for-each>
<!--select repeatable groups for each product-->
<xsl:for-each select="key('key_product_group',#id )[generate-id() = generate-id(key('key_manufacturer_group',
concat(#id,
#manufacturer_code,
#manufacturer)))]">
<xsl:value-of select="#manufacturer_code"/>
<xsl:value-of select="$var_delimiter"/>
<xsl:value-of select="#manufacturer"/>
<xsl:value-of select="$var_delimiter"/>
<!--count number of groups for each product and add blanks for the difference between the max and current instance-->
<xsl:variable name="var_max_manufacturer_group_instance" select="count(key('key_product_group',#id )[generate-id() = generate-id(key('key_manufacturer_group',
concat(#id,
#manufacturer_code,
#manufacturer)))])"/>
<xsl:call-template name="loop_pmf_data">
<xsl:with-param name="count" select="$var_max_manufacturer_group - $var_max_manufacturer_group_instance"/>
</xsl:call-template>
</xsl:for-each>
<xsl:value-of select="$var_line_break"/>
</xsl:for-each>
</xsl:template>
<!--recursive loop templates for file header and data-->
<xsl:template name="loop_pcat_header">
<xsl:param name="count" select="1"/>
<xsl:param name="limit" select="$count+1"/>
<xsl:if test="$count > 0">
<xsl:value-of select="concat('category_code_',$limit - $count)"/>
<xsl:value-of select="$var_delimiter"/>
<xsl:value-of select="concat('category_',$limit - $count)"/>
<xsl:value-of select="$var_delimiter"/>
<xsl:call-template name="loop_pcat_header">
<xsl:with-param name="count" select="$count - 1"/>
<xsl:with-param name="limit" select="$limit"/>
</xsl:call-template>
</xsl:if>
</xsl:template>
<xsl:template name="loop_pcat_data">
<xsl:param name="count" select="1"/>
<xsl:param name="limit" select="$count+1"/>
<xsl:if test="$count > 0">
<xsl:value-of select="$var_delimiter"/>
<xsl:value-of select="$var_delimiter"/>
<xsl:call-template name="loop_pcat_data">
<xsl:with-param name="count" select="$count - 1"/>
<xsl:with-param name="limit" select="$limit"/>
</xsl:call-template>
</xsl:if>
</xsl:template>
<xsl:template name="loop_pmf_header">
<xsl:param name="count" select="1"/>
<xsl:param name="limit" select="$count+1"/>
<xsl:if test="$count > 0">
<xsl:value-of select="concat('manufacturer_code_',$limit - $count)"/>
<xsl:value-of select="$var_delimiter"/>
<xsl:value-of select="concat('manufacturer_',$limit - $count)"/>
<xsl:value-of select="$var_delimiter"/>
<xsl:call-template name="loop_pmf_header">
<xsl:with-param name="count" select="$count - 1"/>
<xsl:with-param name="limit" select="$limit"/>
</xsl:call-template>
</xsl:if>
</xsl:template>
<xsl:template name="loop_pmf_data">
<xsl:param name="count" select="1"/>
<xsl:param name="limit" select="$count+1"/>
<xsl:if test="$count > 0">
<xsl:value-of select="$var_delimiter"/>
<xsl:value-of select="$var_delimiter"/>
<xsl:call-template name="loop_pmf_data">
<xsl:with-param name="count" select="$count - 1"/>
<xsl:with-param name="limit" select="$limit"/>
</xsl:call-template>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
when applied to this XML
<?xml version="1.0" encoding="UTF-8"?>
<products>
<product id="10" name="Widget 1" price="15.99" category_code="T" category="Toys" manufacturer_code="SC1" manufacturer="Some Company 1" />
<product id="10" name="Widget 1" price="15.99" category_code="E" category="Electronics" manufacturer_code="SC1" manufacturer="Some Company 1" />
<product id="10" name="Widget 1" price="15.99" category_code="T" category="Toys" manufacturer_code="SC2" manufacturer="Some Company 2" />
<product id="10" name="Widget 1" price="15.99" category_code="E" category="Electronics" manufacturer_code="SC2" manufacturer="Some Company 2" />
<product id="10" name="Widget 1" price="15.99" category_code="T" category="Toys" manufacturer_code="SC3" manufacturer="Some Company 3" />
<product id="10" name="Widget 1" price="15.99" category_code="E" category="Electronics" manufacturer_code="SC3" manufacturer="Some Company 3" />
<product id="11" name="Widget 2" price="21.99" category_code="V" category="Video Games" manufacturer_code="SC4" manufacturer="Some Company 4" />
<product id="12" name="Widget 3" price="10.99" category_code="T" category="Toys" manufacturer_code="SC1" manufacturer="Some Company 2" />
</products>
generates the following output:
id,name,price,category_code_1,category_1,category_code_2,category_2,manufacturer_code_1,manufacturer_1,manufacturer_code_2,manufacturer_2,manufacturer_code_3,manufacturer_3,
10,Widget 1,15.99,T,Toys,E,Electronics,SC1,Some Company 1,SC2,Some Company 2,SC3,Some Company 3,
11,Widget 2,21.99,V,Video Games,,,SC4,Some Company 4,,,,,
12,Widget 3,10.99,T,Toys,,,SC1,Some Company 2,,,,,

Add two hexBinarys with XPATH 1.0

my xml document looks somewhat like that (Values are both xsl:hexBinary):
<Offsets>
<Offset>
<Name>ErrorOffset</Name>
<Value>DD</Value>
</Offset>
<Offset>
<Name>OtherOffset</Name>
<Value>FF</Value>
</Offset>
</Offsets>
<Value>
<Name>Error1</Name>
<Code>01</Code>
</Value>
<Value>
<Name>Error2</Name>
<Code>02</Code>
<Offset>ErrorOffset</Offset>
</Value>
now i want to transform this to a new xml file:
<Value>
<Name>Error1</Name>
<Code>01</Code>
</Value>
<Value>
<Name>Error2</Name>
<Code>DF</Code>
</Value>
All that should happen is adding <Offset> to the basic <Value>. But plain + returns NaN and sum() expects only one parameter. XSLT and XPATH are quite nice, but it goes on my nerves that easy operations like adding two hex values just dont work as easy as it should.
I never developed a conersión function for hex numbers. This is an example of a function that is reverse to the example of Dimitre. I think it would be possible to further reduce the stylesheet. It is also worth noting that the conversion function can be parameterized and generalized to any base.
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:key name="offset" match="Offset/Value" use="../Name" />
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="Offsets|Offset" />
<xsl:template match="Code/text()[../../Offset]" >
<xsl:variable name="code">
<xsl:call-template name="hex2dec">
<xsl:with-param name="num" select="." />
</xsl:call-template>
</xsl:variable>
<xsl:variable name="offset">
<xsl:call-template name="hex2dec">
<xsl:with-param name="num" select="key('offset',../../Offset)" />
</xsl:call-template>
</xsl:variable>
<xsl:call-template name="dec2hex">
<xsl:with-param name="dec" select="$code + $offset" />
</xsl:call-template>
</xsl:template>
<xsl:template name="hex2dec">
<xsl:param name="num" />
<xsl:param name="hex" select="translate($num,'abcdef','ABCDEF')"/>
<xsl:param name="acc" select="0" />
<xsl:choose>
<xsl:when test="string-length($hex)">
<xsl:call-template name="hex2dec">
<xsl:with-param name="hex" select="substring($hex,2,string-length($hex))" />
<xsl:with-param name="acc" select="$acc * 16 + string-length(substring-before('0123456789ABCDEF',substring($hex,1,1)))" />
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$acc" />
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template name="dec2hex">
<xsl:param name="dec" />
<xsl:if test="$dec >= 16">
<xsl:call-template name="dec2hex">
<xsl:with-param name="dec" select="floor($dec div 16)" />
</xsl:call-template>
</xsl:if>
<xsl:value-of select="substring('0123456789ABCDEF', ($dec mod 16) + 1, 1)" />
</xsl:template>
</xsl:stylesheet>
Edit: Recently I just realized here is cross references. Therefore keys should be used.
Here is a solution, which combines the conversion of hex to decimal values as present in FXSL with a borrowed non-FXSL template for convertion of decimal to hex.
This transformation:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:f="http://fxsl.sf.net/"
xmlns:func-transform2="f:func-transform2"
exclude-result-prefixes="xsl f func-transform2"
>
<xsl:import href="transform-and-sum.xsl"/>
<xsl:import href="hex-to-decimal.xsl"/>
<!-- to be applied on testTransform-and-sum2.xml -->
<xsl:output method="text"/>
<func-transform2:func-transform2/>
<xsl:template match="/">
<xsl:variable name="vdecSum">
<xsl:call-template name="transform-and-sum">
<xsl:with-param name="pFuncTransform"
select="document('')/*/func-transform2:*[1]"/>
<xsl:with-param name="pList" select="/*/*"/>
</xsl:call-template>
</xsl:variable>
<xsl:call-template name="toHex">
<xsl:with-param name="decimalNumber" select="$vdecSum"/>
</xsl:call-template>
</xsl:template>
<xsl:template match="func-transform2:*" mode="f:FXSL">
<xsl:param name="arg1" select="0"/>
<xsl:call-template name="hex-to-decimal">
<xsl:with-param name="pxNumber" select="$arg1"/>
</xsl:call-template>
</xsl:template>
<xsl:template name="toHex">
<xsl:param name="decimalNumber" />
<xsl:if test="$decimalNumber >= 16">
<xsl:call-template name="toHex">
<xsl:with-param name="decimalNumber" select="floor($decimalNumber div 16)" />
</xsl:call-template>
</xsl:if>
<xsl:value-of select="substring($hexDigits, ($decimalNumber mod 16) + 1, 1)" />
</xsl:template>
</xsl:stylesheet>
when applied on this XML document:
<t>
<hexNum>1001</hexNum>
<hexNum>0FA3</hexNum>
</t>
produces the correct, wanted result:
1FA4
This is how I do it without using XSLT:
Hex to number
160 * translate(substring(Offsets/Offset/Value,1,1),
'0123456789ABCDEFabcdef',
'0000000000111111111111') +
16 * translate(substring(Offsets/Offset/Value,1,1),
'0123456789ABCDEFabcdef',
'0123456789012345012345') +
10 * translate(substring(Offsets/Offset/Value,2,1),
'0123456789ABCDEFabcdef',
'0000000000111111111111') +
translate(substring(Offsets/Offset/Value,2,1),
'0123456789ABCDEFabcdef',
'0123456789012345012345')
The reverse function is simpler:
concat(
substring('0123456789ABCDEF', valueDecimal / 16 ,1) ,
substring('0123456789ABCDEF', valueDecimal mod 16 ,1)
)
Both assume your hex digits always have two digits.