In XSLT (2.0 preferred), how to convert a sequence of numbers in an array to a range of numbers, eg. <a>1, 2, 3, 6, 7, 9</a> to <a>1-3, 6-7, 9</a>?
You can simply use for-each-group select="$sequence" group-adjacent="xs:integer(.) - position()":
<xsl:template match="a">
<xsl:copy>
<xsl:value-of separator=", ">
<xsl:for-each-group select="tokenize(., ',\s*')"
group-adjacent="xs:integer(.) - position()">
<xsl:sequence
select="
if (not(current-group()[2])) then
.
else
concat(., '-', current-group()[last()])"/>
</xsl:for-each-group>
</xsl:value-of>
</xsl:copy>
</xsl:template>
Transforms
<a>1, 2, 3, 6, 7, 9</a>
into
<a>1-3, 6-7, 9</a>
Related
Here I want to count/Increment the repeated emp_no using for each.
and I just want to concatenate emp_no with duplicate no count..
Here is my XSLT style sheet.
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:map="http://www.w3.org/2005/xpath-functions/map"
xmlns:array="http://www.w3.org/2005/xpath-functions/array" exclude-result-prefixes="#all"
version="3.0">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:param name="input"/>
<xsl:variable name="newline" select="'
'"/>
<xsl:template match="/" name="xsl:initial-template">
<xsl:variable name="input-as-map" select="parse-json($input)" as="map(*)"/>
<root>
<file1>
<xsl:for-each select="$input-as-map?file1?*">
<xsl:value-of select="$newline"/>
<xsl:value-of select="concat(?emp_no,',',count(?emp_no))"/>
<xsl:value-of select="$newline"/>
</xsl:for-each>
</file1>
</root>
</xsl:template>
</xsl:stylesheet>
input json:-
{
"file1": [
{
"emp_no": "0002"
},
{
"emp_no": "0009"
},
{
"emp_no": "0002"
},
{
"emp_no": "0009"
},
{
"emp_no": "0009"
}
]
}
Expected output:-
<root>
<file1>
0002,1
0009,1
0002,2
0009,2
0009,3
</file1>
</root>
Any suggestions also would be helpful.
One solution would be to replace the xsl:for-each with xsl:iterate, maintaining a parameter value containing a map from emp_no to an integer counter.
Another would be to convert the array to XML and use xsl:number.
Or you could use fn:fold-left(), again maintaining a map from emp_no to an integer counter as you step through the array.
None of them particularly easy, I'm afraid: perhaps someone else has a better idea.
Here is Mike's third suggestion spelled out:
<file>
<xsl:value-of
select="fold-left(
parse-json($json)?file1?*,
map{},
function($am, $m) {
let $c := head($am)($m?emp_no),
$new-am := map:put(head($am), $m?emp_no, if (empty($c)) then 1 else $c + 1)
return ($new-am, tail($am), $m?emp_no || ',' || $new-am($m?emp_no))
}
) => tail()"
separator="
"/>
</file>
Please, can you help me how to convert negative/positive decimal value to hexadecimal in xslt/xslt 2.0 ?
This is what i tried but does not work with negative numbers/decimals,
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template name="ConvertDecToHex">
<xsl:param name="index" />
<xsl:if test="$index > 0">
<xsl:call-template name="ConvertDecToHex">
<xsl:with-param name="index" select="floor($index div 16)" />
</xsl:call-template>
<xsl:choose>
<xsl:when test="$index mod 16 < 10">
<xsl:value-of select="$index mod 16" />
</xsl:when>
<xsl:otherwise>
<xsl:choose>
<xsl:when test="$index mod 16 = 10">A</xsl:when>
<xsl:when test="$index mod 16 = 11">B</xsl:when>
<xsl:when test="$index mod 16 = 12">C</xsl:when>
<xsl:when test="$index mod 16 = 13">D</xsl:when>
<xsl:when test="$index mod 16 = 14">E</xsl:when>
<xsl:when test="$index mod 16 = 15">F</xsl:when>
<xsl:otherwise>A</xsl:otherwise>
</xsl:choose>
</xsl:otherwise>
</xsl:choose>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
This transformation:
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:my="my:my">
<xsl:output method="text"/>
<xsl:template match="/">
<xsl:value-of select="my:intToHex(53), my:intToHex(-53)" separator="
"/>
</xsl:template>
<xsl:function name="my:intToHex" as="xs:string">
<xsl:param name="pInt" as="xs:integer"/>
<xsl:variable name="vMinusOneHex64" select="18446744073709551615"/>
<xsl:variable name="vCompl" select=
"if($pInt ge 0)
then $pInt
else $vMinusOneHex64 + $pInt +1"/>
<xsl:sequence select=
"if ($vCompl eq 0)
then '0'
else
concat(if ($vCompl gt 16)
then my:intToHex($vCompl idiv 16)
else '',
substring('0123456789ABCDEF',
($vCompl mod 16) +1,
1)
)"/>
</xsl:function>
</xsl:stylesheet>
When applied on any source XML document (ignored), produces the desired result:
35
FFFFFFFFFFFFFFCB
I need to convert a hexadecimal value to an integer value, how can I do that using XSLT?
For example, if the input is hexadecimal FF, my output should be 255.
Converting hexadecimal to decimal in pure XSLT 1.0:
<xsl:template name="hex2num">
<xsl:param name="hex"/>
<xsl:param name="num" select="0"/>
<xsl:param name="MSB" select="translate(substring($hex, 1, 1), 'abcdef', 'ABCDEF')"/>
<xsl:param name="value" select="string-length(substring-before('0123456789ABCDEF', $MSB))"/>
<xsl:param name="result" select="16 * $num + $value"/>
<xsl:choose>
<xsl:when test="string-length($hex) > 1">
<xsl:call-template name="hex2num">
<xsl:with-param name="hex" select="substring($hex, 2)"/>
<xsl:with-param name="num" select="$result"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$result"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
Edit: Sorry, my bad I just noted the OP asked for hex to decimal and not the other way around.
hexToDecimal template for XSLT-1.0:
<xsl:template name="hexToDecimal">
<xsl:param name="hex"/>
<xsl:variable name="dec"
select="string-length(substring-before('0123456789ABCDEF', substring($hex,1,1)))"/>
<xsl:choose>
<xsl:when test="$hex = ''">0</xsl:when>
<xsl:otherwise>
<xsl:variable name="rest">
<xsl:call-template name="hexToDecimal">
<xsl:with-param name="hex">
<xsl:value-of select="substring($hex,2)"/>
</xsl:with-param>
</xsl:call-template>
</xsl:variable>
<xsl:value-of select="$dec * math:power(16, string-length($hex) - 1) + $rest"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
This needs math:power from exslt http://exslt.org/math/
hexToDecimal function for XSLT-2.0:
<xsl:function name="f:hexToDec">
<xsl:param name="hex"/>
<xsl:variable name="dec"
select="string-length(substring-before('0123456789ABCDEF', substring($hex,1,1)))"/>
<xsl:choose>
<xsl:when test="matches($hex, '([0-9]*|[A-F]*)')">
<xsl:value-of
select="if ($hex = '') then 0
else $dec * f:power(16, string-length($hex) - 1) + f:hexToDec(substring($hex,2))"/>
</xsl:when>
<xsl:otherwise>
<xsl:message>Provided value is not hexadecimal...</xsl:message>
<xsl:value-of select="$hex"/>
</xsl:otherwise>
</xsl:choose>
</xsl:function>
<xsl:function name="f:power">
<xsl:param name="base"/>
<xsl:param name="exp"/>
<xsl:sequence
select="if ($exp lt 0) then f:power(1.0 div $base, -$exp)
else if ($exp eq 0)
then 1e0
else $base * f:power($base, $exp - 1)"
/>
</xsl:function>
Leaving this here, also it was not asked for, it might still be useful.
There is no directly implemented function in xslt, so you would have to write it yourself.
Here's a template for XSLT 1.0:
<xsl:template name="decimalToHex">
<xsl:param name="dec"/>
<xsl:if test="$dec > 0">
<xsl:call-template name="decimalToHex">
<xsl:with-param name="dec" select="floor($dec div 16)"/>
</xsl:call-template>
<xsl:value-of select="substring('0123456789ABCDEF', (($dec mod 16) + 1), 1)"/>
</xsl:if>
</xsl:template>
You call it like this:
<xsl:call-template name="decimalToHex">
<xsl:with-param name="dec">4095</xsl:with-param>
</xsl:call-template>
And a function for XSLT 2.0:
<xsl:function name="f:decimalToHex">
<xsl:param name="dec"/>
<xsl:if test="$dec > 0">
<xsl:value-of
select="f:decimalToHex(floor($dec div 16)),substring('0123456789ABCDEF', (($dec mod 16) + 1), 1)"
separator=""
/>
</xsl:if>
</xsl:function>
Which you can call like this:
<xsl:value-of select="f:decimalToHex(4095)"/>
Be aware that you have to declare the namespace for the function in you stylesheet.
This function "karusell:isCurrentDateTimeBetweenDates" never gets called. Why cant i use it for filtering? If a call i alone and store it in a variable it work. The value of the variable is false.
<xsl:variable name="bannerList" select="verticaldata/contents/content[ karusell:isCurrentDateTimeBetweenDates( 'Thursday', '01' , 'Friday', '03') ][position() <= 5]" />
Edit:
How can the string be returned?
The filtering
The function
<xsl:function name="karusell:isCurrentDateTimeBetweenDates">
<xsl:param name="startDay"/>
<xsl:param name="startHour" />
<xsl:param name="endDay"/>
<xsl:param name="endHour" />
<xsl:variable name="currentDateTime" select="current-dateTime() " />
<xsl:variable name="todayIndex" select="karusell:getDayIndex(format-dateTime($currentDateTime , '[F]'))" />
<xsl:variable name="startDayIndex" select="karusell:getDayIndex($startDay)" />
<xsl:variable name="endDayIndex" select="karusell:getDayIndex($endDay)" />
<xsl:variable name="startDate" select= "$currentDateTime - ( $todayIndex - $startDayIndex )*xs:dayTimeDuration('P1D')"/>
<xsl:variable name="endDate" select= "$currentDateTime + ( $endDayIndex - $todayIndex )*xs:dayTimeDuration('P1D')"/>
<xsl:variable name="startDateTime" select="format-dateTime($startDate, concat('[Y0001]-[M01]-[D01]T', $startHour ,':00:00')) cast as xs:dateTime"/>
<xsl:variable name="endDateTime" select="format-dateTime($endDate, concat('[Y0001]-[M01]-[D01]T', $endHour ,':00:00')) cast as xs:dateTime"/>
<xsl:value-of select="$currentDateTime >= $startDateTime and $currentDateTime < $endDateTime" />
</xsl:function>
Use xsl:sequence, not xsl:value-of to return values of the data type your expression has. So replace
<xsl:value-of select="$currentDateTime >= $startDateTime and $currentDateTime < $endDateTime" />
with
<xsl:sequence select="$currentDateTime >= $startDateTime and $currentDateTime < $endDateTime" />
Additionally you can get better error diagnosis if you define the return type of your function with <xsl:function name="pf:foo" as="xs:boolean">..</xsl:function>.
I am trying to make an xslt that can convert a position (vector3) via a quaternion to a new position. I have made the following set-up, but I am retrieving NaN's for very small values close to 0. How can I calculate further with the values that come from the quaternion to right-vector calculations?
<xsl:template name="object_markingtape_position">
<xsl:param name="sign"/> <!-- left or right (-1 or 1) -->
<xsl:param name="quaternion"/> <!-- quaternion in x,y,z,w -->
<xsl:variable name="rightvec">
<xsl:call-template name="QuatToRight">
<xsl:with-param name="x" select="$quaternion/ns:x"/>
<xsl:with-param name="y" select="$quaternion/ns:y"/>
<xsl:with-param name="z" select="$quaternion/ns:z"/>
<xsl:with-param name="w" select="$quaternion/ns:w"/>
</xsl:call-template>
</xsl:variable>
Right vector y: <xsl:value-of select="number($rightvec/y)"/> <!-- results in a value with 1.5435434E-04 -->
<xsl:element name="position" namespace="{$ns}">
<xsl:element name="x" namespace="{$ns}">
<xsl:value-of select="$sign * 1.5 * $rightvec/x + ns:position/ns:x"/>
</xsl:element>
<xsl:element name="y" namespace="{$ns}"> <!-- results into NaN -->
<xsl:value-of select="$sign * 1.5 * $rightvec/y + ns:position/ns:y"/>
</xsl:element>
<xsl:element name="z" namespace="{$ns}">
<xsl:value-of select="$sign * 1.5 * $rightvec/z + ns:position/ns:z"/>
</xsl:element>
</xsl:element>
</xsl:template>
<!-- Functionality for calculating the vectors from a quaternion -->
<!-- From http://nic-gamedev.blogspot.nl/2011/11/quaternion-math-getting-local-axis.html -->
<xsl:template name="QuatToRight">
<xsl:param name="x"/>
<xsl:param name="y"/>
<xsl:param name="z"/>
<xsl:param name="w"/>
<xsl:element name="vec3">
<xsl:element name="x">
<xsl:value-of select="1 - 2 * ($x * $y - $w * $z)"/>
</xsl:element>
<xsl:element name="y">
<xsl:value-of select="2 * ($x * $y + $w * $z)"/>
</xsl:element>
<xsl:element name="z">
<xsl:value-of select="2 * ($x * $z - $w * $y)"/>
</xsl:element>
</xsl:element>
</xsl:template>
An example of xml values that can come in are the following:
<Item>
<position>
<x>-106.6172</x>
<y>0.780673563</y>
<z>-13.0446815</z>
</position>
<rotation> <!-- this is where the quaternion param is filled with -->
<x>0.0810996</x>
<y>0.354339659</y>
<z>-0.207844481</z>
<w>-0.908111751</w>
</rotation>
</Item>
If you can't find a better solution you might have to resort to using extension functions.
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:quat="urn:myExtension" xmlns:msxsl="urn:schemas-microsoft-com:xslt" exclude-result-prefixes="quat msxsl" version="1.0">
<xsl:param name="x"/>
<xsl:param name="y"/>
<xsl:param name="z"/>
<xsl:param name="w"/>
<xsl:template match="*">
<xsl:value-of select="quat:calcQuat($x, $y, $z, $w)"/>
</xsl:template>
<msxsl:script implements-prefix="quat" language="c#">
<![CDATA[
public string calcQuat(double x, double y, double z, double w)
{
double result = 1 - 2 * (x * y - w * z)
return Double.ToString(result)
}
]]>
</msxsl:script>
There are three problems I can see:
the variable rightvec contains an XML fragment, but is then used as a node-set - this requires a conversion to work usually (but it depends on the XSLT processor)
the named template object_markingtape_position references an element ns:position in the current context, so its result depends on where it is called - better to pass the position as a parameter;
the named tamplate QuatToRight generates a vec3 element containing sub-elements with the values, but its result is then used as if there was not vec3 intermediate element.
Using the .NET XSLT processor, this XSLT:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:msxsl="urn:schemas-microsoft-com:xslt" exclude-result-prefixes="msxsl"
>
<xsl:output method="xml" indent="yes"/>
<xsl:template name="object_markingtape_position">
<xsl:param name="sign"/> <!-- left or right (-1 or 1) -->
<xsl:param name="quaternion"/> <!-- quaternion in x,y,z,w -->
<xsl:param name="position"/> <!-- position in x,y,z -->
<xsl:variable name="rightvecFragment">
<xsl:call-template name="QuatToRight">
<xsl:with-param name="x" select="$quaternion/x"/>
<xsl:with-param name="y" select="$quaternion/y"/>
<xsl:with-param name="z" select="$quaternion/z"/>
<xsl:with-param name="w" select="$quaternion/w"/>
</xsl:call-template>
</xsl:variable>
<xsl:variable name="rightvec" select="msxsl:node-set($rightvecFragment)"/>
<xsl:element name="position">
<xsl:element name="x">
<xsl:value-of select="$sign * 1.5 * $rightvec/x + $position/x"/>
</xsl:element>
<xsl:element name="y">
<!-- results into NaN -->
<xsl:value-of select="$sign * 1.5 * $rightvec/y + $position/y"/>
</xsl:element>
<xsl:element name="z">
<xsl:value-of select="$sign * 1.5 * $rightvec/z + $position/z"/>
</xsl:element>
</xsl:element>
</xsl:template>
<!-- Functionality for calculating the vectors from a quaternion -->
<!-- From http://nic-gamedev.blogspot.nl/2011/11/quaternion-math-getting-local-axis.html -->
<xsl:template name="QuatToRight">
<xsl:param name="x"/>
<xsl:param name="y"/>
<xsl:param name="z"/>
<xsl:param name="w"/>
<xsl:element name="x">
<xsl:value-of select="1 - 2 * ($x * $y - $w * $z)"/>
</xsl:element>
<xsl:element name="y">
<xsl:value-of select="2 * ($x * $y + $w * $z)"/>
</xsl:element>
<xsl:element name="z">
<xsl:value-of select="2 * ($x * $z - $w * $y)"/>
</xsl:element>
</xsl:template>
<xsl:template match="/*">
<xsl:call-template name="object_markingtape_position">
<xsl:with-param name="sign" select="1"/>
<xsl:with-param name="position" select="position"/>
<xsl:with-param name="quaternion" select="rotation"/>
</xsl:call-template>
</xsl:template>
</xsl:stylesheet>
applied to the example input produces:
<position>
<x>-104.63717236709732</x>
<y>1.4331220235568977</y>
<z>-12.129909788264223</z>
</position>
I resolved this with some data loss, but it is the cleanest solution so far. I used format-number().
<xsl:template name="QuatToRight">
<xsl:param name="x"/>
<xsl:param name="y"/>
<xsl:param name="z"/>
<xsl:param name="w"/>
<xsl:element name="vec3">
<xsl:element name="x">
<xsl:value-of select="format-number(1 - 2 * ($x * $y - $w * $z),'0.000000')"/>
</xsl:element>
<xsl:element name="y">
<xsl:value-of select="format-number(2 * ($x * $y + $w * $z),'0.000000')"/>
</xsl:element>
<xsl:element name="z">
<xsl:value-of select="format-number(2 * ($x * $z - $w * $y),'0.000000')"/>
</xsl:element>
</xsl:element>
</xsl:template>