views:

109

answers:

2

Hi, 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.

+1  A: 

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
Dimitre Novatchev
thank you again, i will have a look in your fxsl library!
atamanroman
+1  A: 

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"&gt; 

<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.

Alejandro
with your hex2dez and dimitres dez2hex template provided, it works. thank you and dimitre!
atamanroman