tags:

views:

153

answers:

1

I have the following XML:

<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" href="disp.xsl"?>
<root>
 <node type="a"> 
  <value>4</value>
 </node>
 <node type="b">
  <value>2</value>
 </node>
 <node type="a">
  <value>3</value>
 </node>
 <node type="b">
  <value>1</value>
 </node>
</root

I want to produce a report which totals the value elements of each type and keeps a running total. I.E, I want:

type: a total:7 cumulative total:7
type: b total:3 cumulative total:10

Here is my XSL:

<?xml version="1.0" encoding="ISO-8859-1"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"&gt;
<xsl:key name="eachtype" match="node" use="@type" />
<xsl:template match="/root">
  <html>
  <body>
   <xsl:for-each select="node">
    Value: <xsl:value-of select="value"/> (Cumulative value: <xsl:variable name="temp" select="sum(preceding-sibling::node/value)+value"/><xsl:value-of select="$temp"/>)<br />
   </xsl:for-each>
   <hr />
   <xsl:for-each select="node">
    <xsl:variable name="thisType" select="@type"/>
    type: <xsl:value-of select="$thisType" /> Value: <xsl:value-of select="value"/> (Cumulative value: <xsl:variable name="temp2" select="sum(preceding-sibling::node/value)+value"/><xsl:value-of select="$temp2"/>)<br />
   </xsl:for-each>
   <hr />
      <xsl:for-each select="node[generate-id(.)=generate-id(key('eachtype',@type)[1])]">
    <xsl:variable name="thisType" select="@type"/>
    type: <xsl:value-of select="$thisType" /> Total: <xsl:value-of select="sum(/root/node[@type=$thisType]/value)"/> (Cumulative value: <xsl:variable name="temp2" select="sum(preceding-sibling::value)+value"/><xsl:value-of select="$temp2"/>)<br />
   </xsl:for-each>
  </body>
  </html>
</xsl:template>
</xsl:stylesheet>

Which produces the following output:

Value: 4 (Cumulative value: 4)
Value: 3 (Cumulative value: 7)
Value: 2 (Cumulative value: 9)
Value: 1 (Cumulative value: 10)

--------------------------------------------------------------------------------
type: a Value: 4 (Cumulative value: 4)
type: a Value: 3 (Cumulative value: 7)
type: b Value: 2 (Cumulative value: 9)
type: b Value: 1 (Cumulative value: 10)

--------------------------------------------------------------------------------
type: a Total: 7 (Cumulative value: 4)
type: b Total: 3 (Cumulative value: 2)

I can't find a way to get a correct value for cumulative total in the last two lines. Are there any XSL veterans out there who can help me in my frist attempt with XSL?

+1  A: 

This transformation:

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform"&gt;
 <xsl:output method="text"/>

 <xsl:key name="kType" match="@type" use="."/>

 <xsl:template match="/">
  <xsl:for-each select=
   "*/*/@type
          [generate-id()
          =
           generate-id(key('kType', .)[1])
           ]">
    <xsl:value-of  select=
     "concat('type: ', .,
             ' total: ', sum(/*/*[@type = current()]/value),

             ' cumulative total: ',
             /*/*[@type = current()][last()]/value
            +
             sum(/*/*[@type = current()][last()]/preceding-sibling::*/value),

             '&#xA;'
              )
     "/>
  </xsl:for-each>
 </xsl:template>
</xsl:stylesheet>

when applied on the provided XML document:

<root>
    <node type="a">
        <value>4</value>
    </node>
    <node type="a">
        <value>3</value>
    </node>
    <node type="b">
        <value>2</value>
    </node>
    <node type="b">
        <value>1</value>
    </node>
</root>

produces the wanted, correct result:

type: a total: 7 cumulative total: 7
type: b total: 3 cumulative total: 10

Do note that this solution is not efficient. I will provide a more efficient (recursive) one after the brake. :)

Dimitre Novatchev
Thanks Dimitre this works well when the XML nodes are ordered by type. But, when the order of types is different, e.g. type a,b,a,b, the cumulative total goes wrong. I've tried sorting the for-each, but this does not work because the for-each is working on the key.
Dave
@Dave: You must provide a complete example -- with the xml when the nodes are not ordered by type -- and what the result should be in this case.
Dimitre Novatchev
Sorry Dimitre, I've now updated the XML in the original question. I really appreciate your help! I've looked into changing how the XML is generated (this is just a handcrafted example) but can't ensure the XML is created in node type order.
Dave
@Dave: There is nothing wrong with you having a different problem -- whenever you *specify* that problem (provide the corresponding XML document and what result you want) I'd love to show you the solution. At this moment my solution is to the problem you specified -- not to any other problems you have.
Dimitre Novatchev