tags:

views:

69

answers:

3

I need a loop up to a specific node position() and sum of these nodes. For example:

  • if my position is 2 then node[1] + node[2]
  • if my position is 5 then node[1] + node[2] + node[3] + node[4] + node[5]

How can that be done in XSLT?

do have following xml

<?xml-stylesheet type="text/xsl" href="sum.xsl"?><root><FIDetail Node1="5" Node2="2" Node3="9" Node4="5" Node5="1" Node6="6" Node7="5" Node8="5" Node9="12" Node10="6" Node11="4" Node12="8" Node13="4" Node14="6" Node15="5" Node16="6" Node17="2" Node18="7" Node19="4" Node20="5" Node21="4" Node22="6" Node23="4" Node24="11" Node25="5" Node26="1" Node27="7" Node28="1" Node29="4" Node30="2" Node31="5" Node32="2" Node33="6" Node34="4" Node35="7" Node36="7" Node37="9" Node38="10" Node39="3" Node40="8" Node41="8" Node42="5" Node43="5" Node44="2" Node45="5" Node46="12" Node47="9" Node48="14" Node49="18" Node50="1"/></root>

i am trying to show output as below

5 sum of Node1

7 sum of Node1 + Node2

16 sum of Node1 + Node2 + Node3

.... sum of Node1 + ... + Node50

any one please help me that what will be XSL

not working xsl is as below

<?xml version="1.0" encoding="UTF-8"?>

    <xsl:for-each select="//FIDetail/@*">
        <br/>
        <xsl:value-of select="sum(preceding-sibling::*) + ."/>
        =
        <xsl:for-each select="preceding-sibling::*">
            <xsl:value-of select="name()"/> +
        </xsl:for-each>
        <xsl:value-of select="name()"/>
    </xsl:for-each>

</xsl:template>

+3  A: 

The XPath query for the sum of the context node and the preceding siblings is . + sum(./preceding-sibling::*).

E.g. if you have the following XML ...

<a>
   <b id="a">1</b>
   <b id="b">2</b>
   <b id="c">3</b>
   <b id="d">4</b>
   <b id="e">5</b>
   <b id="f">6</b>
</a>

... you can get the sum of the node with id="c" and the preceding nodes using the expression

/a/b[@id="c"]/(. + sum(./preceding-sibling::*))
jarnbjo
+1. I think that `sum(.| preceding-sibling::*)` is more elegant.
Tomalak
Ya know, you should edit your question and add that :)
Nic Gibson
@Kedar: It was not clear from your original question that you were talking about attributes. What you are trying to achieve is not possible without perhaps relying on implementation details of a specific XSL transformer. The XML specification states that attribute order is irrelevant (chapter 3.1) and the behaviour of the XPath position() function is not specified for attribute nodes.
jarnbjo
+2  A: 

Not pretty, but it works:

<table>
  <xsl:for-each select="@*">
    <tr>
      <xsl:variable name="pos" select="position()"/>
      <xsl:variable name="earlier" select="../@*[position() &lt; $pos]"/>
      <td>
        <xsl:value-of select="sum($earlier) + ."/>
      </td>
      <td>
        <xsl:for-each select="$earlier">
          <xsl:value-of select="name()"/> +
        </xsl:for-each>
        <xsl:value-of select="name()"/>
      </td>
    </tr>
  </xsl:for-each>
</table>
Marc Gravell
Thanks its working, I appreciate to your quick response.
Kedar
A: 

You may be able to cobble together a solution that appears to work, but there's an inherent problem, and your solution is going to break.

The problem is that attributes in XML don't have significant order. You can't rely on attributes being presented to any process, in or out of XSLT, in the same order they appear in the text. Frankly, I'm surprised that XSLT even allows you to use position() in an attribute predicate.

(Incidentally, this is the reason that the identity transform uses that odd pattern select="node()|@*". The @* is required because node() doesn't match attributes. node() doesn't match attributes because attributes aren't nodes. Nodes have position, and attributes don't.)

If your application depends on the ordering of attributes, it's broken and you need to redesign it.

There's a way out, though, if you can rely on the attributes' names to provide some kind of ordering, as shown in your example:

<xsl:variable name="atts" select="@*"/>
<xsl:for-each select="$atts">
  <xsl:sort select="substring-after(name(), 'Node')" data-type="number"/>
  <td>
    <xsl:variable name="this" select="number(substring-after(name(), 'Node'))"/>
    <xsl:value-of select="sum($atts[
                  number(substring-after(name(), 'Node')) &lt;= $this])"/>
  </td>
</xsl:for-each>

Is that ugly? You bet. And it breaks if you ever change your attribute naming scheme in the slightest. But it'll work however the attributes are ordered.

Robert Rossney
Thanks Robert Rossney, This one is also important for me.
Kedar