Please review my code and give me your advise on this:
XML file: content.xml:
<content>
<page id="page-1">
<!-- ... -->
<block-center>
<block-center-row id="block-center-row-1">
<cd>
<title>Empire Burlesque</title>
<artist>Bob Dylan</artist>
<country>USA</country>
<company>Columbia</company>
<price>10.90</price>
<year>1985</year>
</cd>
<cd>
<title>Hide your heart</title>
<artist>Bonnie Tyler</artist>
<country>UK</country>
<company>CBS Records</company>
<price>9.90</price>
<year>1988</year>
</cd>
</block-center-row>
<block-center-row id="block-center-row-2">
<block-center-colunm id="block-center-2-1">
<book>
<title>Book Title1</title>
<author>Book Author1</author>
</book>
<book>
<title>Book Title2</title>
<author>Book Author2</author>
</book>
<book>
<title>Book Title3</title>
<author>Book Author3</author>
</book>
</block-center-colunm>
<block-center-colunm id="block-center-2-2">
<seminar>
<author>Seminar author1</author>
<durable>3</durable>
</seminar>
<seminar>
<author>Seminar author2</author>
<durable>1.5</durable>
</seminar>
<seminar>
<author>Seminar author3</author>
<durable>2</durable>
</seminar>
<seminar>
<author>Seminar author4</author>
<durable>3</durable>
</seminar>
</block-center-colunm>
</block-center-row>
</block-center>
</page>
<!-- ... -->
</content>
XSL file: block-center-1-1.xsl:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template name="block-center-row-1">
<div class="block-center-row">
<h2>My CD Collection</h2>
<table border="1" width="100%">
<tr bgcolor="#9acd32">
<th>Title</th>
<th>Artist</th>
</tr>
<xsl:for-each select="
content/page[#id=$pageId]/block-center/
block-center-row[#id='block-center-row-1']/cd
">
<tr>
<td>
<xsl:value-of select="title" />
</td>
<xsl:choose>
<xsl:when test="price > 10">
<td bgcolor="#ff00ff">
<xsl:value-of select="artist" />
</td>
</xsl:when>
<xsl:when test="price > 9">
<td bgcolor="#cccccc">
<xsl:value-of select="artist" />
</td>
</xsl:when>
<xsl:otherwise>
<td>
<xsl:value-of select="artist" />
</td>
</xsl:otherwise>
</xsl:choose>
</tr>
</xsl:for-each>
</table>
</div>
</xsl:template>
</xsl:stylesheet>
XSL file block-center.xsl:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:import href="block-center-1-1.xsl" />
<xsl:template name="block-center">
<!-- if put here. It work properly -->
<div class="block-center">
<xsl:for-each select="content/page[#id=$pageId]/block-center/block-center-row">
<xsl:choose>
<!-- // I does not work here -->
<xsl:when test="#id='block-center-row-1'">
<xsl:call-template name="block-center-row-1" />
</xsl:when>
</xsl:choose>
</xsl:for-each>
</div>
</xsl:template>
</xsl:stylesheet>
I dont know why It does not work (not out put data) if I call <xsl:call-template> inside <xsl:for-each> loop. Otherwise, It's OK.
I have a few remarks to make. First off, it is a bad idea to make format implications in the XML. Having elements that are called <block-center> or <block-center-colunm> is not only unnecessarily verbose, it will also make your head hurt as soon as their contents is not going to be displayed in a block in the center anymore.
Second, let go of <xsl:call-template> and <xsl:for-each>. They may seem convenient if you have procedural programming background, but they are the wrong choice. Use <xsl:apply-templates> instead, it leads to code that's cleaner and easier to understand.
Now to your XSL. Your second XSL (block-center.xsl) - what does it do? Why do you have two separate XSL files? Also, it is missing a variable or parameter declaration. I've used:
<xsl:variable name="pageId" select="'page-1'" />
for my tests. It also has only one template (<xsl:template name="block-center">) that is never actually being called, so it did not do anything for me. I've added
<xsl:template match="/">
<xsl:call-template name="block-center" />
</xsl:template>
so it would at least do anything.
Your other XSL file (block-center-1-1.xsl) has a for-each loop that is not looking at the right context. You already are in the following context when you enter the template "block-center-row-1":
content/page[#id=$pageId]/block-center/block-center-row
so all you need to do in the for-each loop is:
<xsl:for-each select="cd">
and it will start to output all <cd> nodes.
Many thanks for your euthusiasm!
It's now works properly if I change in block-center-1-1.xsl file
TO
OR
Related
Im doing an assignment for University (so im new to XSL coding) in making a quasi ecommerce site, and will provide as much detail as i can so it makes sense.
Sample XML Data:
<Items>
<Item>
<ItemID>50001</ItemID>
<ItemName>Samsung Galaxy S4</ItemName>
<ItemPrice>629</ItemPrice>
<ItemQty>14</ItemQty>
<ItemDesc>4G Mobile</ItemDesc>
<QtyHold>0</QtyHold>
<QtySold>1</QtySold>
</Item>
<Item>
<ItemID>50002</ItemID>
<ItemName>Samsung Galaxy S5</ItemName>
<ItemPrice>779</ItemPrice>
<ItemQty>21</ItemQty>
<ItemDesc>4G Mobile</ItemDesc>
<QtyHold>0</QtyHold>
<QtySold>1</QtySold>
</Item>
</Items>
Website
So the process is, when a person clicks 'Add to Cart' in the top Table, the ItemQty is decreased by 1 on the ItemQty in the XML, while it increases by 1 in the QtyHold in the XML. (QtyHold represents what has been added to the shopping Cart. Thus if QtyHold is >0 then its been added to the Cart)
My problem refers to the 2nd Table (code below), where the Total figure works - only if dealing with 1 Item. Thus, if Item Number '50001' is added a 2nd time, the Total wont change.
<xsl:template match="/">
<fieldset>
<legend>Shopping Cart</legend>
<BR />
<table border="1" id="CartTable" align="center">
<tr><th>Item Number</th>
<th>Price</th>
<th>Quantity</th>
<th>Remove</th></tr>
<xsl:for-each select="/Items/Item[QtyHold > 0]">
<tr><td><xsl:value-of select="ItemID"/></td>
<td>$<xsl:value-of select="ItemPrice"/></td>
<td><xsl:value-of select="QtyHold"/></td>
<td><button onclick="addtoCart({ItemID}, 'Remove')">Remove from Cart</button></td> </tr>
</xsl:for-each>
<tr><td ALIGN="center" COLSPAN="3">Total:</td><td>$<xsl:value-of select="sum(//Item[QtyHold >0]/ItemPrice)"/></td></tr>
</table>
<BR />
<button onclick="Purchase()" class="submit_btn float_l">Confirm Purchase</button>
<button onclick="CancelOrder()" class="submit_btn float_r">Cancel Order</button>
</fieldset>
</xsl:template>
</xsl:stylesheet>
So what needs to happen is within the following code, while it checks if the QtyHold is greater than 0 (which would mean its in the shopping Cart) & to sum these values, it also needs to multiply QtyHold & ItemPrice.
<xsl:value-of select="sum(//Item[QtyHold >0]/ItemPrice)"/>
I tried many variations of Code like this below... but can't seem to make anything work.
select="sum(//Item[QtyHold >0]/ItemPrice)/(QtyHold*ItemPrice"/>
If you are using XSLT 2.0, the expression you could use would be this:
<xsl:value-of select="sum(//Item[QtyHold >0]/(ItemPrice * QtyHold))"/>
However, in XSLT 1.0 that is not allowed. Instead, you could achieve the result you need with an extension function. In particular the "node-set" function. First you would create a variable like this, in which you construct new nodes holding each item total
<xsl:variable name="itemTotals">
<xsl:for-each select="//Item[QtyHold >0]">
<total>
<xsl:value-of select="ItemPrice * QtyHold" />
</total>
</xsl:for-each>
</xsl:variable>
Ideally, you would like to do sum($itemTotals/total), but this won't work, because itemTotals is a "Result Tree Fragment" and the sum function only accepts a node-set. So you use the node-set extension function to convert it. First declare this namespace in your XSLT...
xmlns:exsl="http://exslt.org/common"
Then, your sum function would look like this:
<xsl:value-of select="sum(exsl:node-set($itemTotals)/total)"/>
Alternatively, if you couldn't even use an extension function, you could use the "following-sibling" approach, to select each Item at a time, and keep a running total. So, you would have a template like this:
<xsl:template match="Item" mode="sum">
<xsl:param name="runningTotal" select="0" />
<xsl:variable name="newTotal" select="$runningTotal + ItemPrice * QtyHold" />
<xsl:variable name="nextItem" select="following-sibling::Item[1]" />
<xsl:choose>
<xsl:when test="$nextItem">
<xsl:apply-templates select="$nextItem" mode="sum">
<xsl:with-param name="runningTotal" select="$newTotal" />
</xsl:apply-templates>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$newTotal" />
</xsl:otherwise>
</xsl:choose>
</xsl:template>
And to call it, to get the sum, you just start off by selecting the first node
<xsl:apply-templates select="(//Item)[1]" mode="sum" />
Try this XSLT which demonstrates the various approaches
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"
xmlns:exsl="http://exslt.org/common"
exclude-result-prefixes="exsl">
<xsl:output method="html" indent="yes" />
<xsl:template match="/">
<table border="1" id="CartTable" align="center">
<tr><th>Item Number</th>
<th>Price</th>
<th>Quantity</th>
</tr>
<xsl:for-each select="/Items/Item[QtyHold > 0]">
<tr>
<td><xsl:value-of select="ItemID"/></td>
<td>$<xsl:value-of select="ItemPrice"/></td>
<td><xsl:value-of select="QtyHold"/></td>
</tr>
</xsl:for-each>
<tr>
<td ALIGN="center" COLSPAN="2">Total:</td>
<xsl:variable name="itemTotals">
<xsl:for-each select="//Item[QtyHold >0]">
<total>
<xsl:value-of select="ItemPrice * QtyHold" />
</total>
</xsl:for-each>
</xsl:variable>
<td>
<!-- XSLT 2.0 only: $<xsl:value-of select="sum(//Item[QtyHold >0]/(ItemPrice * QtyHold))"/>-->
$<xsl:value-of select="sum(exsl:node-set($itemTotals)/total)"/>
$<xsl:apply-templates select="(//Item)[1]" mode="sum" />
</td>
</tr>
</table>
</xsl:template>
<xsl:template match="Item" mode="sum">
<xsl:param name="runningTotal" select="0" />
<xsl:variable name="newTotal" select="$runningTotal + ItemPrice * QtyHold" />
<xsl:variable name="nextItem" select="following-sibling::Item[1]" />
<xsl:choose>
<xsl:when test="$nextItem">
<xsl:apply-templates select="$nextItem" mode="sum">
<xsl:with-param name="runningTotal" select="$newTotal" />
</xsl:apply-templates>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$newTotal" />
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
As a final thought, why don't you just a new Total element to each Item element in your XML. Initially, it would be set to 0, like QtyHold. Then, when you increment QtyHold by 1, by what ever process you do, you can also increment Total by the amount held in ItemPrice. That way, you can just sum this Total node to get the overall total, without the need for extension functions or recursive templates.
I am beginner for XSLT, currently I am trying to display list of <department> in each <company> node.
Below is my XML
<employee_data>
<employeedetails id="1">
<company id="1">
<companyname>AOL</companyname>
<department>IT</department>
</company>
<employeename>Patrick</employeename>
<employeedesg>Software Engineer</employeedesg>
<employeesalary>18000</employeesalary>
<employeedoj>10/03/2015</employeedoj>
</employeedetails>
..... similar sets......
..... similar sets......
<employeedetails id="10">
<company id="1">
<companyname>AOL</companyname>
<department>HR</department>
</company>
<employeename>Patricia</employeename>
<employeedesg>HR Assistant</employeedesg>
<employeesalary>18000</employeesalary>
<employeedoj>10/03/2015</employeedoj>
</employeedetails>
</employee_data>
I have written the below XSLT to fetch only company names, but I want to display all the departments from the various companies.
XSLT:
<xsl:key name="companyname" match="/employee_data/employeedetails/company/companyname" use="."/>
<xsl:for-each select="/employee_data/employeedetails/company/companyname[generate-id() = generate-id(key('companyname',.)[1])]">
<tr>
<td>
<xsl:value-of select="../#id"/>
</td>
<td>
<xsl:value-of select="."/>
</td>
</tr>
</xsl:for-each>
If you want to get the department nodes for a company simply use the same key that you have used in the grouping of the companies
<xsl:for-each select="key('companyname',.)">
<xsl:value-of select="../department" />
<br />
</xsl:for-each>
As a side note, you can actually simplify your key. You don't actually need to specify the full path to the company element, just the name will do
<xsl:key name="companyname" match="company" use="companyname"/>
You would only need to consider using a path if there were other company elements at a different level in the XML hierarchy that you didn't want included.
Also note I am matching company and not companyname simply to avoid using the .. parent axis.
Try this XSLT
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="html" />
<xsl:key name="companyname" match="company" use="companyname"/>
<xsl:template match="/">
<xsl:for-each select="/employee_data/employeedetails/company[generate-id() = generate-id(key('companyname', companyname)[1])]">
<tr>
<td>
<xsl:value-of select="#id"/>
</td>
<td>
<xsl:apply-templates select="key('companyname', companyname)" />
</td>
</tr>
</xsl:for-each>
</xsl:template>
<xsl:template match="company">
<xsl:value-of select="department" />
<br />
</xsl:template>
</xsl:stylesheet>
I'm using the following xml information:
<section>
<...>
</section>
<section>
<templateId root="2.16.840.1.113883.10.20.22.2.10" />
<text>
<table id="Appointments">
<tr>
<td id="heading">Appointments</td>
</tr>
<tr>
<td id="content">No future appointments scheduled.</td>
</tr>
</table>
<br />
<table id="Referrals">
<tr>
<td id="heading">Referrals</td>
</tr>
<tr>
<td id="content">No referrals available.</td>
</tr>
</table>
<br />
</text>
<section>
<section>
<...>
</section>
There are multiple section nodes (with their own child nodes, including templateId) within the document. I'm having trouble with this one so I wanted to be specific in the xml information.
and in my xslt file I want to get one particular table out. I'm referencing it the following way (I'm trying to use templates and I'm new to XSL so please bear with me)
<xsl:stylesheet
version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:ms="urn:schemas-microsoft-com:xslt"
xmlns="http://www.w3.org/1999/xhtml">
<xsl:output method="html" indent="yes"/>
<xsl:template match="/">
<xsl:apply-templates select="//section[templateId/#root='2.16.840.1.113883.10.20.22.2.17']/text/table[#id='Appointments']" />
</xsl: template>
<xsl:template match="section[templateId/#root='2.16.840.1.113883.10.20.22.2.17']/text/table[#id='Appointments']">
<div style="float: left; width: 50%;">
<span style="font-weight: bold;">
<xsl:value-of select="tr/td[#id='heading']"/>:
</span>
<br />
<xsl:call-template name="replace">
<xsl:with-param name="string" select="tr/td[#id='content']"/>
</xsl:call-template>
</div>
</xsl:template>
<xsl:template name="replace">
<xsl:param name="string"/>
<xsl:choose>
<xsl:when test="contains($string,'
')">
<xsl:value-of select="substring-before($string,'
')"/>
<br/>
<xsl:call-template name="replace">
<xsl:with-param name="string" select="substring-after($string,'
')"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$string"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
In this particular xml example, the output should be:
Appointments:
No future appointments scheduled.
I'm thinking the match and select need some tweaking but not sure what part.
Also, if the template can be tweaked so that I could pass a parameter with the table/#id value so that I could reuse this one template for a couple of items,that would be even more beneficial (the output for referrals and appointments that are in this example would be the same).
Thanks for any help
This is your XML section root attribute (cut and paste from your XML):
root="2.16.840.1.113883.10.20.22.2.10"
This is your test XSL:
root='2.16.840.1.113883.10.20.22.2.17'
Of course they do not match, one ends with "10", the other with "17"
Changing the data to "17" and correcting the other errors in my comments yields:
<div style="float: left; width: 50%;"><span style="font-weight: bold;">Appointments:
</span><br>No future appointments scheduled.
</div
I have the template
<xsl:template match="node" mode="some_mode">
<xsl:value-of select="child1" />
<xsl:value-of select="child2" />
<xsl:value-of select="child3" />
</xsl:template>
I want to apply the template so it would select all the nodes specified in the template in one case like this
<xsl:apply-templates select="node" mode="some_node" /> <!-- select all inside the node tag -->
and in another case I want to limit the output and for example not to select <child1> or <child2> nodes. Can I do it with a variable or a param? Or do I have to write another template from scratch?
<xsl:apply-templates select="node" mode="some_node" /> <!-- select only some tags from the node tag -->
In other word I will use this templates several times and I want to contorl the output when applying. I can define the variable, but the documentation says that I can't change the value of a variable once it was defined. Probably the param will wrok but I'm not good with it.
I'm afraid that's not possible without modification of the template you have.
You could think about storing template output in a variable and then processing it to remove child1 and child2 values. But I guess you would be unable to guess which part of your output is coming from child1 and/or child2.
Or you can develop another template that performs that alternative action.
EDIT: Another idea:
Maybe it is possible to apply filtering to get rid of child1 and child2 before applying your template (that would be a sort of multi-pass XSL tranformation).
Use the xsl:if instruction inside your template to branch.
Example:
<xsl:template match="node">
<xsl:value-of select="child1" />
<xsl:if test="price > 10">
<xsl:value-of select="child2" />
<xsl:value-of select="child3" />
</xsl:if>
</xsl:template>
I was hoping for a simple example in the question. As of the face-of-it one is not forthcoming, I'll make up a simple example here, which emodies your question, and show you how the xsl:if instruction is the perfect answer.
Let's say you have a music collection like so ...
<?xml version="1.0"?>
<catalog>
<cd>
<title>Empire Burlesque</title>
<artist>Bob Dylan</artist>
<country>USA</country>
<company>Columbia</company>
<price>10.90</price>
<year>1985</year>
</cd>
<cd>
<title>Hide your heart</title>
<artist>Bonnie Tyler</artist>
<country>UK</country>
<company>CBS Records</company>
<price>9.90</price>
<year>1988</year>
</cd>
</catalog>
And you want to output a html table of your CD's listing title and price. Normally you want both title and price, but on some conditions (say price is less than $10), you want to suppress the output of price, and just have some static text in its place, like "cheaper than 10.00".
So your expected output should be:
<?xml version="1.0" encoding="utf-8"?>
<table>
<tr>
<td>Empire Burlesque</td>
<td>cheaper than 10.00</td>
</tr>
<tr>
<td>Hide your heart</td>
<td>9.90</td>
</tr>
</table>
The style-sheet to produce this transformation could be:
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="/">
<table>
<xsl:apply-templates select="//cd"/>
</table>
</xsl:template>
<xsl:template match="cd">
<tr>
<td><xsl:value-of select="title"/></td>
<xsl:if test="price < 10">
<td><xsl:value-of select="price"/></td>
</xsl:if>
<xsl:if test="not(price < 10)">
<td>cheaper than 10.00</td>
</xsl:if>
</tr>
</xsl:template>
</xsl:stylesheet>
And there you have the perfect answer - how to apply the whole template or just parts of it depending on a condition. In this case the condition is that the price is less than $10.
I recommend using separate templates with different match patterns and/or in different modes. This is to be preferred to using conditionals and thus writing messy and potentially buggy code:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:template match="/">
<xsl:apply-templates mode="allChildren"/>
==========
<xsl:apply-templates mode="child3Only"/>
</xsl:template>
<xsl:template match="node" mode="allChildren">
<xsl:copy-of select="*"/>
</xsl:template>
<xsl:template match="node" mode="child3Only">
<xsl:copy-of select="child3"/>
</xsl:template>
</xsl:stylesheet>
When this transformation is applied on the following document (similar to the one you used in a previous question):
<document>
<node>
<child1>Child 1</child1>
<child2>Child 2</child2>
<child3>Child 3</child3>
</node>
<anotherNode />
</document>
the wanted result is produced:
<child1>Child 1</child1><child2>Child 2</child2><child3>Child 3</child3>
==========
<child3>Child 3</child3>
The source XML (this is just foobar data, in reality it is thousands of rows wich can be both positive and negative):
<accounting>
<entry id="1">
<accounting_date>2010-10-29</accounting_date>
<transfer_date>2010-10-29</transfer_date>
<description>Start balance</description>
<vat>0</vat>
<sum>87287</sum>
</entry>
<entry id="2">
<accounting_date>2011-01-24</accounting_date>
<transfer_date>2011-02-17</transfer_date>
<description>Bill 1</description>
<vat>175</vat>
<sum>875</sum>
</entry>
<entry id="3">
<accounting_date>2011-01-31</accounting_date>
<transfer_date>2011-01-18</transfer_date>
<description>Bill 2</description>
<vat>350</vat>
<sum>1750</sum>
</entry>
</accounting>
I want to transform this XML to an HTML table to display to the user. Most of the transformation is just putting values in the right places, but the balance-field is giving me headache.
My XSLT (that does not work):
<table>
<tr>
<th>Accounting date</th>
<th>Description</th>
<th>Sum</th>
<th>Balanche</th>
</tr>
<xsl:for-each select="/accounting/entry">
<tr>
<td><xsl:value-of select="accounting_date" /></td>
<td><xsl:value-of select="description" /></td>
<td><xsl:value-of select="sum" /></td>
<td><xsl:value-of select="sum(../entry[position() < current()/position()]/sum)" /></td><!-- This XPath is the problem! -->
</tr>
</xsl:for-each>
</table>
Expected result:
<table>
<tr>
<th>Accounting date</th>
<th>Description</th>
<th>Sum</th>
<th>Balanche</th>
</tr>
<tr>
<td>2010-10-29</td>
<td>Start balance</td>
<td>87287</td>
<td>87287</td>
</tr>
<tr>
<td>2011-01-24</td>
<td>Bill 1</td>
<td>875</td>
<td>88162</td>
</tr>
<tr>
<td>2011-01-31</td>
<td>Bill 2</td>
<td>1750</td>
<td>89912</td>
</tr>
</table>
Chrome is blank, and Firefox gives me:
Error loading stylesheet: XPath parse failure: Name or Nodetype test expected:
I'm stuck, please help. :)
The best solution might depend a bit on whether you are using XSLT 1.0 or XSLT 2.0. You really need to say, since at present there's a roughly even mix of both in use in the field. (It seems you're running it in the browser, which suggests you want a 1.0 solution, so that's what I've given you).
But either way, recursion is your friend. In this case, "sibling recursion" where you write a template to process an entry, and it does apply-templates to process the next entry, passing the total so far as a parameter: something like this
<xsl:template match="entry">
<xsl:param name="total-so-far" select="0"/>
<tr>
<td><xsl:value-of select="accounting_date" /></td>
<td><xsl:value-of select="description" /></td>
<td><xsl:value-of select="sum" /></td>
<td><xsl:value-of select="$total-so-far + sum"/></td><
</tr>
<xsl:apply-templates select="following-sibling::entry[1]">
<xsl:with-param name="total-so-far" select="$total-so-far + sum"/>
</xsl:apply-templates>
</xsl:template>
Then you need to start the process off with
<xsl:template match="accounting">
<table>
<xsl:apply-templates select="entry[1]"/>
</table>
</xsl:template>
If there are thousands of rows then this could cause stack overflow in an XSLT processor that doesn't do tail call optimisation. I've no idea whether the XSLT processors in today's browsers implement this optimisation or not.
Alternatively you can use the preceding-sibling axes
<table>
<tr>
<th>Accounting date</th>
<th>Description</th>
<th>Sum</th>
<th>Balanche</th>
</tr>
<xsl:for-each select="/accounting/entry">
<tr>
<td>
<xsl:value-of select="accounting_date" />
</td>
<td>
<xsl:value-of select="description" />
</td>
<td>
<xsl:value-of select="sum" />
</td>
<td>
<xsl:value-of select="sum(preceding-sibling::*/sum)+sum" />
</td>
</tr>
</xsl:for-each>
</table>
In addition to the correct answer by #Michael Kay, here is a general template/function from FXSL to use for computing running totals. Its DVC variant will never (for practical purposes) crash due to stack overflow. With DVC (Divide and Conquer) recursion, processing a sequence of 1000000 (1M) items requires maximum stack depth of only 19.
Here is an example of using the scanl template:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:f="http://fxsl.sf.net/"
xmlns:myAdd="f:myAdd"
xmlns:myParam="f:myParam"
>
<xsl:import href="scanlDVC.xsl"/>
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<myAdd:myAdd/>
<myParam:myParam>0</myParam:myParam>
<xsl:template match="/">
<xsl:variable name="vFun" select="document('')/*/myAdd:*[1]"/>
<xsl:variable name="vZero" select="document('')/*/myParam:*[1]"/>
<xsl:call-template name="scanl">
<xsl:with-param name="pFun" select="$vFun"/>
<xsl:with-param name="pQ0" select="$vZero" />
<xsl:with-param name="pList" select="/*/num"/>
</xsl:call-template>
</xsl:template>
<xsl:template match="myAdd:*" mode="f:FXSL">
<xsl:param name="pArg1" select="0"/>
<xsl:param name="pArg2" select="0"/>
<xsl:value-of select="$pArg1 + $pArg2"/>
</xsl:template>
</xsl:stylesheet>
When this transformation is applied on the following XML file:
<nums>
<num>01</num>
<num>02</num>
<num>03</num>
<num>04</num>
<num>05</num>
<num>06</num>
<num>07</num>
<num>08</num>
<num>09</num>
<num>10</num>
</nums>
the correct result (running totals) is produced:
<el>0</el>
<el>1</el>
<el>3</el>
<el>6</el>
<el>10</el>
<el>15</el>
<el>21</el>
<el>28</el>
<el>36</el>
<el>45</el>
<el>55</el>
Using it for the provided XML document:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:f="http://fxsl.sf.net/"
xmlns:myAdd="f:myAdd"
xmlns:myParam="f:myParam"
>
<xsl:import href="scanlDVC.xsl"/>
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<myAdd:myAdd/>
<myParam:myParam>0</myParam:myParam>
<xsl:template match="/">
<xsl:variable name="vFun" select="document('')/*/myAdd:*[1]"/>
<xsl:variable name="vZero" select="document('')/*/myParam:*[1]"/>
<xsl:call-template name="scanl">
<xsl:with-param name="pFun" select="$vFun"/>
<xsl:with-param name="pQ0" select="$vZero" />
<xsl:with-param name="pList" select="/*/*/sum"/>
</xsl:call-template>
</xsl:template>
<xsl:template match="myAdd:*" mode="f:FXSL">
<xsl:param name="pArg1" select="0"/>
<xsl:param name="pArg2" select="0"/>
<xsl:value-of select="$pArg1 + $pArg2"/>
</xsl:template>
</xsl:stylesheet>
and the correct result is produced::
<el>0</el>
<el>87287</el>
<el>88162</el>
<el>89912</el>