Add two hexBinary with XPATH 1.0

my xml document looks something like this (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 convert 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 to add <Offset> to the base <Value> . But normal + returns NaN , and sum() expects only one parameter. XSLT and XPATH are pretty nice, but it gets on my nerves that simple operations like adding two hexadecimal values ​​just don't work as simple as they should.

+1
source share
3 answers

I never developed a conersión function for hexadecimal numbers. This is an example of a function that addresses the Dimitre example. 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 realized that these are cross-references. Therefore, you should use the keys.

+1
source

Here is a solution that combines the conversion of hexadecimal values ​​to decimal values ​​represented in FXSL > with a borrowed pattern without FXSL to convert decimal to hexadecimal.

This conversion is :

 <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 to this XML document :

 <t> <hexNum>1001</hexNum> <hexNum>0FA3</hexNum> </t> 

produces the correct, desired result :

 1FA4 
+1
source

Here's 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 inverse function is simpler:

 concat( substring('0123456789ABCDEF', valueDecimal / 16 ,1) , substring('0123456789ABCDEF', valueDecimal mod 16 ,1) ) 

Both suggest that your hexadecimal digits always have two digits.

0
source

Source: https://habr.com/ru/post/1415042/


All Articles