views:

705

answers:

4

Hello everyone,

I am stuck with a problem which seems stupid but I cannot find out the solution... With XLST, I need to sum a list of values calculated by a template. So I stored these values in a tree structure (a list of "Number" elements contained in a root element "Numbers"). But whatever I try to do with this self-made list, it will return either nothing, 0 or an error...

Does someone know what I am doing wrong ?

<!-- create the tree fragment -->
<xsl:variable name="_subTotals">
    <Numbers>
        <xsl:for-each select="List">
            <xsl:variable name="_Size">
                <xsl:call-template name="GetSize">
                    <xsl:with-param name="_value" select="@value"/>
                </xsl:call-template>
            </xsl:variable>
            <Number>
                <xsl:value-of select="$_Size"/>
            </Number>
        </xsl:for-each>
    </Numbers>
</xsl:variable>

<!-- this returns an error: expression must result into a node-set -->
<xsl:message terminate="no">
    <xsl:value-of select="sum($_subTotals/Numbers/Number)"/>
</xsl:message>

<!-- transform the tree fragment into a node-set
<xsl:variable name="_Total" select="msxsl:node-set($_subTotals)"/>

<!-- returns nothing -->
<xsl:for-each select="$_Total/Numbers/Number">
    <xsl:message terminate="no">
        <xsl:value-of select="@value"/>
    </xsl:message>
</xsl:for-each>

<!-- returns 0 -->
<xsl:value-of select="sum($_Total/Numbers/Number)"/>
A: 

Thank you very much Dimitre ! That's exactly what solved my problem : the X-Path expression I used in the sum function.

But why does this work :

<xsl:value-of select="sum($vNums/*/*)"/>

and not that :

<xsl:value-of select="sum($vNums/nums/num)"/>

?

Julien
A: 

Recursion is the usual answer in functional languages. Something like:

<xsl:template name='totalRest>
  <xsl:variable nane='sub'>
    <!-- Use for-each to set local new context node -->
    <xsl:for-each select='following::List[1]'>
      <xsl:call-template name='totalRest'/>
    </xsl:for-each>
  </xsl:variable>
  <xsl:variable name='this'>
    <xsl:call-template name="GetSize">
      <xsl:with-param name="_value" select="@value"/>
    </xsl:call-template>
  </xsl:variable>

  <xsl:value-of select='$sub+$this' />

</xsl:template>

Without knowing more of GetSize and the input document it is hard to be more specific about the right XPath to set the context node for the recursive call.

Richard
Sorry for not being very specific, but I tried to simplify the problem. Actually I use an attribute of the List elements as an ID to get other attribute values somewhere in the input document, which are then the parameters (there are 3) of the template GetSize.
Julien
But thank you for your answer, Richard ! I will try to implement it.
Julien
The recursion approach is general, however the details (and ensuring context node for XPath is OK) and make it arbitrarily complex :-(
Richard
A: 

Thank you for you answer, Richard. It is an interesting way to do this recursively. However, the problem is that there is a context on "List" - which makes usage of a template difficult.

Here is how it looks :

<xsl:template name='totalRest'>
    <xsl:param name="_Id"/>
  <xsl:variable name='sub'>
    <!-- Use for-each to set local new context node -->
    <xsl:for-each select='following::BlockList/Block/Parameter[@Id=$_Id]/List[1]'>
      <xsl:call-template name='totalRest'/>
    </xsl:for-each>
  </xsl:variable>
  <xsl:variable name="_RecordParameter">
     <xsl:value-of select="@RecordParameter"/>
    </xsl:variable>
    <xsl:variable name="_tmpParaType">
     <xsl:value-of select="/BlockList/Block/Parameter[@u16_ParaId=$_RecordParameter]/@u8_ParaType"/>
    </xsl:variable>
    <xsl:variable name="_tmpDataType">
     <xsl:value-of select="substring($_tmpParaType, 4)"/>
    </xsl:variable>
    <xsl:variable name="_tmpArrayDimension">
     <xsl:value-of select="/BlockList/Block/Parameter[@u16_ParaId=$_RecordParameter]/@u8_ArrayDimension"/>
    </xsl:variable>
    <xsl:variable name="_tmpFormat">
     <xsl:value-of select="/BlockList/Block/Parameter[@u16_ParaId=$_RecordParameter]/@Format"/>
    </xsl:variable>
  <xsl:variable name='this'>
    <xsl:call-template name="GetByteSize">
     <xsl:with-param name="_ArrayDimension" select="$_tmpArrayDimension"/>
     <xsl:with-param name="_Format" select="$_tmpFormat"/>
     <xsl:with-param name="_DBDataType" select="$_tmpDataType"/>
    </xsl:call-template>
  </xsl:variable>
  <xsl:value-of select='$sub+$this' />
</xsl:template>

And I call the template this way :

<xsl:variable name="_Id">
    <xsl:value-of select="@Id"/>
</xsl:variable>
<xsl:call-template name="totalRest">
    <xsl:with-param name="_Id" select="$_Id"/>
</xsl:call-template>

But it does not work... Any idea ?

Julien
Please provide a complete (but the minimum possible) example of the XSLT code and the XML document. If you cannot express the problem precisely, why do you think anyone would be able to understand what the problem is?
Dimitre Novatchev
@Julien: And please, do so right in your question (you can edit it). The answers section is for actual answers only. Please also delete your posts unless they are answers. Thanks!
Tomalak
Sorry Dimitre, I thought I had simplified the problem enough (and not too much) to make it easily understandable - apparently it worked since you knew exactly what I needed !Thanks for your advice Tomalak - I'll know this for the next time. I discovered this site recently : it is great !
Julien
A: 

Here is a quick way how to sum dynamically-generated values (Probably the template you are calling is not producing the expected results? If so, you must ask another question and provide both the code and the XML document on which the code operates. Without these no one can help and guesses would not be useful.):

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
 xmlns:ext="http://exslt.org/common"
 >
 <xsl:output method="text"/>

    <xsl:template match="/">
      <xsl:variable name="vrtfNums">
         <nums>
           <num>1</num>
           <num>2</num>
         </nums>
      </xsl:variable>

      <xsl:variable name="vNums" select="ext:node-set($vrtfNums)"/>

      <xsl:value-of select="sum($vNums/*/*)"/>
    </xsl:template>
</xsl:stylesheet>

When the above transformation is applied on any XML document (ignored), the wanted result is produced:

3
Dimitre Novatchev