I am trying to get the sum. Here is the xslt code.
<xsl:template match="Entry">
<xsl:if test="position() <= 10">
<tr>
<td>
<xsl:value-of select="substring-before(#Value,'||')"/>
</td>
<td>
<xsl:value-of select="format-number(substring(substring-after(#Value,'||||'),1,10),'#.#')"/>
</td>
</tr>
</xsl:if>
</xsl:template>
above code will fillter data as two coloums. It is ok. Now I need to get the sum of <xsl:value-of select="format-number(substring(substring-after(#Value,'||||'),1,10),'#.#')"/>
I am from procedural programming. I read many articles but I still coudnt figure out that how to get the sum of this. can anybody help me?
Here is the xml
<TopHoldings Currency="xxx">
<Entry Type="CName||C||S||Fund Weight (%)||Benchmark weight (%)" Value="Ab||U||||1.2170000000000||" Date="8/31/2011" />
here is the whole xslt
<table style="width:50%;font-size:12px;" cellspacing="0" cellpadding="0">
<tr style="width:50%; text-align:left;background-color:E6F1F9;">
<th> </th>
<th> % of funds </th>
</tr>
<xsl:apply-templates select="$items">
<xsl:sort select="format-number(substring(substring-after(#Value,'||||'),1,10),'#.#')" order="descending"/>
<xsl:sort select="substring-before(#Value,'||')"/>
</xsl:apply-templates>
</table>
</body>
</html>
</xsl:template>
<xsl:template match="Entry">
<xsl:if test="position() <= 10">
<tr>
<td>
<xsl:value-of select="substring-before(#Value,'||')"/>
</td>
<td>
<xsl:value-of select="format-number(substring(substring-after(#Value,'||||'),1,10),'#.#')"/>
</td>
</tr>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
When you need to sum the values of multiple values on XSLT 1.0 you have to rely on recursion [EDIT: in XSLT 1.0 the function sum it is also available] (in XSLT 2.0 there is an XPath function sum()).
The following template performs the sum of the given elements through the elements-to-sum parameter, extracting the value to sum from the attribute #Value as you specified.
<?xml version="1.0" encoding="utf-8" ?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">
<xsl:output method="html" />
<xsl:template match="TopHoldings">
<table>
<xsl:call-template name="sum">
<xsl:with-param name="elements-to-sum"
select="Entry" />
</xsl:call-template>
</table>
</xsl:template>
<xsl:template name="sum">
<xsl:param name="elements-to-sum" />
<xsl:param name="sum" select="'0'" />
<!-- Store in variables the following operations to avoid performing them more than once -->
<xsl:variable name="current-element" select="$elements-to-sum[1]" />
<xsl:variable name="current-value" select="format-number(substring(substring-after($current-element/#Value,'||||'),1,10),'#.#')" />
<!-- Output the table row -->
<tr>
<td><xsl:value-of select="substring-before($current-element/#Value, '||')" /></td>
<td><xsl:value-of select="$current-value" /></td>
</tr>
<!-- Determine whether continue -->
<xsl:choose>
<!-- Case END: we have just one element to sum so we perform the sum and we output the desired result -->
<xsl:when test="count($elements-to-sum) = 1">
<tr>
<td>Result:</td>
<td><xsl:value-of select="$current-value + $sum" /></td>
</tr>
</xsl:when>
<!-- Case RECURSION : we call this template again adding the current value to the sum and removing the first element from the parameter elements-to-sum -->
<xsl:otherwise>
<xsl:call-template name="sum">
<xsl:with-param name="elements-to-sum"
select="$elements-to-sum[position() > 1]" />
<xsl:with-param name="sum"
select="$sum + $current-value" />
</xsl:call-template>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
I assumed that you wanted to display the result of the sum as a new row in the same table, if that is not the case and you want to display the result elsewhere the solution would be slightly different. Tell me if this solution is acceptable to you or you need to display the sum outside the element.
NOTE: Instead of doing those 'complex' string operations in the attribute #Value, I would consider splitting all the information within that attribute into different attributes.
Just use the XPath function sum(), it is available in both XPath 2.0 for XSLT 2.0 and XPath 1.0 for XSLT 1.0 as described in http://www.w3.org/TR/xpath/#function-sum. If the numbers you want to get the sum of are attributes "Value" to an element "Entry" use sum(//Entry/#Value)" to grab all and sum up. Change this to get the 10 elements you want in your xml data.
Related
I am trying to implement a logic which would do choosing a value if it matches the condition. The problem is, if my list contains more elements than one, it wouldn't display the value which matches the condition, but only the first one. Could you provide some advice? thanks. I need this to implement multiple filtering by conditions. Meaning that in this for-each I would display only books with year 2008, but in another table I could also use 2010 from the list of years. The below example is a part of the template for one table which uses 2008, but I will have multiple tables where I would filter books by 2010 as well.
XSL:
<xsl:for-each select="years">
<xsl:choose>
<xsl:when test="year=2008">
<td class="year"><xsl:value-of select="year"/></td>
</xsl:when>
</xsl:choose>
</xsl:for-each>
XML:
<book>
<title>Professional ASP.NET 4 in C# and VB</title>
<author>Bill Evjen, Scott Hanselman, Devin Rader</author>
<country>USA</country>
<price>580</price>
<years>
<year>2010</year>
<year>2008</year>
</years>
</book>
This one would populate 2010 into my HTML, not 2008 as I would expect. Is this doable at all?
Update:
I've tried this approach for example to filter against multiple conditions separately:
<xsl:for-each select="library/book">
<tr>
<td class="filterTd title"><xsl:value-of select="title"/></td>
<td class="filterTd author"><xsl:value-of select="author"/></td>
<td class="filterTd price"><xsl:value-of select="price"/></td>
<xsl:for-each select="years/year[.2008]">
<td class="year">
<xsl:value-of select="."/>
</td>
</xsl:for-each>
<xsl:for-each select="years/year[.2010]">
<td class="year">
<xsl:value-of select="."/>
</td>
</xsl:for-each>
</tr>
</xsl:for-each>
</table>
This would not populate any td at all in my case..
If you only want to display books for a specific year, you would put a condition in the xsl:for-each that selects the book
<xsl:for-each select="library/book[years/year=$year]">
Where $year is a variable (or parameter) containing the year you want
Do note, if you know you are dealing with a specific year, then you don't actually need to do <xsl:value-of select="year" />, you can just output the year value you are working with.
Try this XSLT
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="html" indent="yes" />
<xsl:template match="/">
<xsl:call-template name="table">
<xsl:with-param name="year" select="2008" />
</xsl:call-template>
</xsl:template>
<xsl:template name="table">
<xsl:param name="year" />
<table>
<xsl:for-each select="library/book[years/year=$year]">
<tr>
<td class="filterTd title"><xsl:value-of select="title"/></td>
<td class="filterTd author"><xsl:value-of select="author"/></td>
<td class="filterTd price"><xsl:value-of select="price"/></td>
<td class="year">
<xsl:value-of select="$year"/>
</td>
</tr>
</xsl:for-each>
</table>
</xsl:template>
</xsl:stylesheet>
Note, you could also use a key here to look up books by year
<xsl:key name="booksByYear" match="book" use="years/year" />
Then, the xsl:for-each to select books, looks like this:
<xsl:for-each select="key('booksByYear', $year)">
I have an XML doc with two sets of data in it. I need to output a HTML table that combines the data.
The XML looks like this:
<?xml version="1.0" encoding="UTF-8"?>
<fdata>
<RepairHistory>
<RepairList>
<RepairJob>
<RepairRef>56740408</RepairRef>
<RepairDescription>Baths</RepairDescription>
<RepairReportedDate>20180515</RepairReportedDate>
<RepairCompletedDate></RepairCompletedDate>
<RepairStartDate>20180515</RepairStartDate>
</RepairJob>
<RepairJob>
<RepairRef>56735043</RepairRef>
<RepairDescription>Basins</RepairDescription>
<RepairReportedDate>20180515</RepairReportedDate>
<RepairCompletedDate></RepairCompletedDate>
<RepairStartDate>20180515</RepairStartDate>
</RepairJob>
<RepairJob>
<RepairRef>56740415</RepairRef>
<RepairDescription>Showers</RepairDescription>
<RepairReportedDate>20180515</RepairReportedDate>
<RepairCompletedDate></RepairCompletedDate>
<RepairStartDate>20180515</RepairStartDate>
</RepairJob>
</RepairList>
</RepairHistory>
<map>
<entry>
<string>GUID</string>
<string>be0f53f5-7a09-47cd-9928-0c865b6450a5</string>
</entry>
<entry>
<string>JOBNUMBER</string>
<string>56740408</string>
</entry>
</map>
<map>
<entry>
<string>GUID</string>
<string>5ce2e8fe-7735-4f98-b3a9-3bd386edb338</string>
</entry>
<entry>
<string>JOBNUMBER</string>
<string>56740415</string>
</entry>
</map>
</fdata>
JOBNUMBER has to match RepairRef. Not all of the data at the bottom matches the data at the top.
I need to output the data in a table as follows:
Date Ref Details GUID
20180515 56740408 Baths be0f53f5-7a09-47cd-9928-0c865b6450a5
20180515 56735043 Basins No GUID
20180515 56740415 Showers 5ce2e8fe-7735-4f98-b3a9-3bd386edb338
I have tried to output the table using the following xsl, but it doesn't work...
<xsl:template match="/">
<h3>Repair history</h3>
<table>
<tr>
<th>Date</th>
<th>Ref</th>
<th>Details</th>
<th>GUID</th>
</tr>
<xsl:apply-templates select ="//RepairJob" />
</table>
</xsl:template>
<xsl:template match="RepairJob">
<xsl:variable name="JobNumber" select="RepairRef" />
<tr>
<td><xsl:value-of select="RepairStartDate"/></td>
<td><xsl:value-of select="RepairRef"/></td>
<td><xsl:value-of select="RepairDescription"/></td>
<td>
<xsl:choose>
<xsl:when test="count(//map//entry[2]//string[2] = $JobNumber) > 0">
<xsl:value-of select="//map//entry[2]//string[2] = $JobNumber"/>
</xsl:when>
<xsl:otherwise>
<xsl:text>No GUID</xsl:text>
</xsl:otherwise>
</xsl:choose>
</td>
</tr>
</xsl:template>
I can see that I am not selecting the correct node to display, but the matching is not working either. Can anyone help?
Your current xsl:value-of evaluates to a boolean ("true" or "false"). The expression you actually want is this...
<xsl:value-of select="//map[entry[2]/string[2] = $JobNumber]/entry[1]/string[2]"/>
The xsl:when needs a tweak too, so the whole xsl:choose should look like this:
<xsl:choose>
<xsl:when test="//map[entry[2]/string[2] = $JobNumber]">
<xsl:value-of select="//map[entry[2]/string[2] = $JobNumber]/entry[1]/string[2]"/>
</xsl:when>
<xsl:otherwise>
<xsl:text>No GUID</xsl:text>
</xsl:otherwise>
</xsl:choose>
However, consider using a key to look up the guids....
<xsl:key name="map" match="map" use="entry[string[1] = 'JOBNUMBER']/string[2]" />
Then your xsl:value-of becomes this
<xsl:value-of select="key('map',$JobNumber)/entry[string[1] = 'GUID']/string[2]"/>
Note, this also allows for the entry elements to be in any order within a map element (i.e. the JOBNUMBER could be the first entry, and the GUID the second)
Try this XSLT
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="html" indent="yes" />
<xsl:key name="map" match="map" use="entry[string[1] = 'JOBNUMBER']/string[2]" />
<xsl:template match="/">
<h3>Repair history</h3>
<table>
<tr>
<th>Date</th>
<th>Ref</th>
<th>Details</th>
<th>GUID</th>
</tr>
<xsl:apply-templates select ="//RepairJob" />
</table>
</xsl:template>
<xsl:template match="RepairJob">
<xsl:variable name="JobNumber" select="RepairRef" />
<tr>
<td><xsl:value-of select="RepairStartDate"/></td>
<td><xsl:value-of select="RepairRef"/></td>
<td><xsl:value-of select="RepairDescription"/></td>
<td>
<xsl:choose>
<xsl:when test="key('map',$JobNumber)">
<xsl:value-of select="key('map',$JobNumber)/entry[string[1] = 'GUID']/string[2]"/>
</xsl:when>
<xsl:otherwise>
<xsl:text>No GUID</xsl:text>
</xsl:otherwise>
</xsl:choose>
</td>
</tr>
</xsl:template>
</xsl:stylesheet>
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.
Could someone please explain why the following does not work.
Here is the xml:
<?xml version="1.0" ?>
<testsuites>
<testsuite errors="0" failures="0" name="test_ui_orchestration" tests="10" time="90.190"/>
<testsuite errors="1" failures="0" name="test_ui_tables" tests="13" time="1115.771"/>
<testsuite errors="0" failures="3" name="test_ui_dashboard" tests="18" time="116.397"/>
</testsuites>
I want to calculate total number of tests, total pass and fail. I have a problem getting total number of failures (failures + errors) and total number of pass (for simplicity: tests - failures). I call the same xml-template for calculating totals passing in different values using "with-param" as follows (section within "Calculate sums" comments):
<?xml version="1.0" encoding="ISO-8859-1"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/testsuites">
<html>
<body>
<!-- Calculate sums start -->
<xsl:variable name="allSum">
<xsl:call-template name="testsSum">
<xsl:with-param name="numTests" select="testsuite/#tests"/>
</xsl:call-template>
</xsl:variable>
<xsl:variable name="failedSum">
<xsl:call-template name="testsSum">
<xsl:with-param name="numTests" select="testsuite/#failures"/>
</xsl:call-template>
</xsl:variable>
<xsl:variable name="errorSum">
<xsl:call-template name="testsSum">
<xsl:with-param name="numTests" select="testsuite/#errors"/>
</xsl:call-template>
</xsl:variable>
<xsl:variable name="failederrorSum">
<xsl:call-template name="testsSum">
<xsl:with-param name="numTests" select="testsuite/#failures + testsuite/#errors"/>
</xsl:call-template>
</xsl:variable>
<!-- Calculate sums ends -->
<h2>Test Results</h2>
<table border="1">
<tr bgcolor="#9acd32">
<th>Test Area</th>
<th>Total</th>
<th>Pass</th>
<th>Fail</th>
<th>Error</th>
<th>Fail and Error</th>
</tr>
<xsl:for-each select="testsuite">
<tr>
<td><xsl:value-of select="#name"/></td>
<td><xsl:value-of select="#tests"/></td>
<td><xsl:value-of select="#tests - (#failures + #errors)"/></td>
<td><xsl:value-of select="#failures"/></td>
<td><xsl:value-of select="#errors"/></td>
<td><xsl:value-of select="#failures + #errors"/></td>
<td> </td>
</tr>
</xsl:for-each>
<tr>
<td><b>All Tests</b></td>
<td><xsl:value-of select="$allSum"/></td>
<td>foo</td>
<!--
<td><xsl:value-of select="$passedSum"/></td>
-->
<td><xsl:value-of select="$failedSum"/></td>
<td><xsl:value-of select="$errorSum"/></td>
<td><xsl:value-of select="$failederrorSum"/></td>
<td></td>
</tr>
</table>
</body>
</html>
</xsl:template>
<xsl:template name="testsSum">
<xsl:param name="numTests"/>
<xsl:choose>
<xsl:when test="$numTests">
<xsl:variable name="recursive_result">
<xsl:call-template name="testsSum">
<xsl:with-param name="numTests" select="$numTests[position() > 1]"/>
</xsl:call-template>
</xsl:variable>
<xsl:value-of select="number($numTests[1]) + $recursive_result"/>
</xsl:when>
<xsl:otherwise><xsl:value-of select="0"/></xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
This produces the following output for the sums (full output is at the end of this post):
41 3 1 0
while it should be:
41 3 1 4
Each individual sum works fine, the corresponding attribute is found. However, when I attempt to add two attributes and pass that to the sum template, it does not work as expected.
It gets worse. The code for generating total passed tests is:
<xsl:variable name="passedSum">
<xsl:call-template name="testsSum">
<xsl:with-param name="numTests"
select="testsuite/#tests - testsuite/#failures"/>
</xsl:call-template>
</xsl:variable>
When the above code is enabled, I get the following error:
Failed to evaluate the expression of variable 'numTests'
I've been searching online and understand that the "select" of "with-param" accepts "An XPath expression that defines the value of the parameter" (from w3schools). An XPath expression can contain arithmetic operations (http://www.w3schools.com/xpath/xpath_operators.asp), so why do the above fail?
Here is full output of the xsl transformation:
Test Area Total Pass Fail Error Fail and Error
----------------------------------------------------------------------
test_ui_orchestration 10 10 0 0 0
test_ui_tables 13 12 0 1 1
test_ui_dashboard 18 15 3 0 3
----------------------------------------------------------------------
All Tests 41 foo 3 1 0
Here is the expected output of the xsl transformation (modified values marked with *):
Test Area Total Pass Fail Error Fail and Error
----------------------------------------------------------------------
test_ui_orchestration 10 10 0 0 0
test_ui_tables 13 12 0 1 1
test_ui_dashboard 18 15 3 0 3
----------------------------------------------------------------------
All Tests 41 37* 3 1 4*
As Stormtroopr says, you haven't shown us the part of the XSLT code that sets the context, and the fact that you left this out suggests you haven't understood how important context is in XSLT. If your XPath expressions select anything at all, we can probably assume that the context item is the testsuites element, in which case path expressions such as testsuite/#failures all select multiple values (a node set containing several attribute nodes). That's fine if the template you are calling expects a node-set. But when you use a node-set containing multiple nodes as input to an arithmetic operation, then:
(a) In XSLT 1.0 it uses the first node in the node-set and ignores the others
(b) In XSLT 2.0 you get a type error saying that what you are doing makes no sense.
Since you're getting an error, I assume you are using XSLT 2.0, though it doesn't seem a very helpful error message (which XSLT processor are you using?)
As for fixing the problem, I can't help with that because you haven't explained what you are trying to achieve - I can't reverse-engineer the requirements from incorrect code.
Since the question has changed dramatically, I've made a new answer.
Your summing template (in fact most of the template) works fine. All you need to do to get those totals down the bottom, you just need to perform addition/subtraction on the variables you've already created:
<xsl:variable name="failederrorSum" select="$failedSum + $errorSum" />
<xsl:variable name="passedSum" select="$allSum - $failederrorSum" />
This stylesheet applied to the input XML:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/testsuites">
<html>
<body>
<!-- Calculate sums start -->
<xsl:variable name="allSum">
<xsl:call-template name="testsSum">
<xsl:with-param name="numTests" select="testsuite/#tests"/>
</xsl:call-template>
</xsl:variable>
<xsl:variable name="failedSum">
<xsl:call-template name="testsSum">
<xsl:with-param name="numTests" select="testsuite/#failures"/>
</xsl:call-template>
</xsl:variable>
<xsl:variable name="errorSum">
<xsl:call-template name="testsSum">
<xsl:with-param name="numTests" select="testsuite/#errors"/>
</xsl:call-template>
</xsl:variable>
<xsl:variable name="failederrorSum" select="$failedSum + $errorSum" />
<xsl:variable name="passedSum" select="$allSum - $failederrorSum" />
<!-- Calculate sums ends -->
<h2>Test Results</h2>
<table border="1">
<tr bgcolor="#9acd32">
<th>Test Area</th>
<th>Total</th>
<th>Pass</th>
<th>Fail</th>
<th>Error</th>
<th>Fail and Error</th>
</tr>
<xsl:apply-templates />
<tr>
<td><b>All Tests</b></td>
<td><xsl:value-of select="$allSum"/></td>
<td><xsl:value-of select="$passedSum"/></td>
<td><xsl:value-of select="$failedSum"/></td>
<td><xsl:value-of select="$errorSum"/></td>
<td><xsl:value-of select="$failederrorSum"/></td>
<td></td>
</tr>
</table>
</body>
</html>
</xsl:template>
<xsl:template match="testsuite">
<tr>
<td><xsl:value-of select="#name"/></td>
<td><xsl:value-of select="#tests"/></td>
<td><xsl:value-of select="#tests - (#failures + #errors)"/></td>
<td><xsl:value-of select="#failures"/></td>
<td><xsl:value-of select="#errors"/></td>
<td><xsl:value-of select="#failures + #errors"/></td>
<td> </td>
</tr>
</xsl:template>
<xsl:template name="testsSum">
<xsl:param name="numTests"/>
<xsl:choose>
<xsl:when test="$numTests">
<xsl:variable name="recursive_result">
<xsl:call-template name="testsSum">
<xsl:with-param name="numTests" select="$numTests[position() > 1]"/>
</xsl:call-template>
</xsl:variable>
<xsl:value-of select="number($numTests[1]) + $recursive_result"/>
</xsl:when>
<xsl:otherwise><xsl:value-of select="0"/></xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
Gives this output:
<html>
<body>
<h2>Test Results</h2>
<table border="1">
<tr bgcolor="#9acd32">
<th>Test Area</th>
<th>Total</th>
<th>Pass</th>
<th>Fail</th>
<th>Error</th>
<th>Fail and Error</th>
</tr>
<tr>
<td>test_ui_orchestration</td>
<td>10</td>
<td>10</td>
<td>0</td>
<td>0</td>
<td>0</td>
<td>
</td>
</tr>
<tr>
<td>test_ui_tables</td>
<td>13</td>
<td>12</td>
<td>0</td>
<td>1</td>
<td>1</td>
<td>
</td>
</tr>
<tr>
<td>test_ui_dashboard</td>
<td>18</td>
<td>15</td>
<td>3</td>
<td>0</td>
<td>3</td>
<td>
</td>
</tr>
<tr>
<td>
<b>All Tests</b>
</td>
<td>41</td>
<td>37</td>
<td>3</td>
<td>1</td>
<td>4</td>
<td/>
</tr>
</table>
</body>
</html>
Which looks like this:
Unfortunately, you've only posted fragments of your XSLT, but I believe the problem is that you are calling testsuite/#failures from the testsuites element.
Rather than returning a number, this is instead returning either a result tree fragment or node-set with the values of the failures attribute for each testsuite element.
As such the traditional numeric operations are failing, as they expect a number and get an RTF or nodeset instead, and attempt to apply the operation giving an unexpected result.
Without knowing what the rest of the code is, I'd suggest the way to fix is to wrap the variable instantiations either in a for-each loop, or even better, in a template that matches on an individual testsuite.
Current List
CurrentList
Access Autocad Burger
Layout Photoshop Sandwich
VisualStudio
I have used a boxed view and edited source like below to get three columns
<xsl:when test="$Position mod 3 = 0">
But the order of items needed is like this
Access Layout VisualStudio
Autocad Photoshop
Burger Sandwich
Can someone suggest a way to achieve this?
This is part of xml
<xsl:template name="NewTRJumbo" ddwrt:ghost="">
<xsl:param name="Position" select="1"/>
<xsl:param name="Collapse" select="."/>
<xsl:choose>
<xsl:when test="$Position mod 3 = 0">
<xsl:text disable-output-escaping="yes"></tr></xsl:text>
<xsl:call-template name="NewTR">
<xsl:with-param name="Collapse" select="$Collapse"/>
<xsl:with-param name="EmptyLine" select="1"/>
</xsl:call-template>
<xsl:text disable-output-escaping="yes"><td></td></tr></xsl:text>
<xsl:call-template name="NewTR">
<xsl:with-param name="Collapse" select="$Collapse"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<td width="1.5%">
</td>
</xsl:otherwise>
</xsl:choose>
I am assuming you have an XML document of elements, for example, something like this
<items>
<item>Access</item>
<item>Autocad</item>
<item>Burger</item>
<item>Layout</item>
<item>Photoshop</item>
<item>Sandwich</item>
<item>VisualStudio</item>
</items>
And you want to group them into 3 columns, but with the items running top-to-bottom first, rather than left-to-right. You current test of $Position mod 3 = 0 is partially along the right lines, but you are actually starting a new row each here, and so end up going left-to-right.
One thing to mention is your use of this line of code
<xsl:text disable-output-escaping="yes"></tr></xsl:text>
It looks like you are simply iterating over the elements, and trying to output a closing tr tag after every third one. XSLT is a functional language, so you need to approach with a different mind set, and do things differentially than you would in a normal procedural language.
Firstly, you need to find the number of rows you will beed to output
<xsl:param name="columns" select="3"/>
<xsl:variable name="items" select="count(/*/item)"/>
<xsl:variable name="rows" select="ceiling($items div $columns)"/>
i.e The rows is the total number of items divided by the number of columns.
Then, you need to select the first item of each row, which is straight-forward because these will be the first 'n' items in your list (Note the use of the mode because the final XSLT will have two templates matching the item element)
<xsl:apply-templates select="item[position() <= $rows]" mode="row"/>
Within the template matching the starting items, you then need to output the current item, and the following sibling items which are in the row
<xsl:apply-templates select=".|following-sibling::item[position() mod $rows = 0]"/>
The only other thing to consider is adding a blank cell to the end of the some rows. This can be done by calculating the number of remaining items compared to the total items
<xsl:variable name="lastItem" select="(position() + $rows * ($columns - 1) - $items)" />
<xsl:if test="$lastItem > 0">
<td colspan="{ceiling($lastItem div $rows)}"></td>
</xsl:if>
Try the following XSLT
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes"/>
<xsl:param name="columns" select="3"/>
<xsl:variable name="items" select="count(/*/item)"/>
<xsl:variable name="rows" select="ceiling($items div $columns)"/>
<xsl:template match="/*">
<table>
<xsl:apply-templates select="item[position() <= $rows]" mode="row"/>
</table>
</xsl:template>
<xsl:template match="item" mode="row">
<tr>
<xsl:apply-templates select=".|following-sibling::item[position() mod $rows = 0]"/>
<xsl:variable name="lastItem" select="(position() + $rows * ($columns - 1) - $items)" />
<xsl:if test="$lastItem > 0">
<td colspan="{ceiling($lastItem div $rows)}"></td>
</xsl:if>
</tr>
</xsl:template>
<xsl:template match="item">
<td>
<xsl:value-of select="."/>
</td>
</xsl:template>
</xsl:stylesheet>
When applied to the XML shown at the beginning of the question, the following is output
<table>
<tr>
<td>Access</td>
<td>Layout</td>
<td>VisualStudio</td>
</tr>
<tr>
<td>Autocad</td>
<td>Photoshop</td>
<td colspan="1"></td>
</tr>
<tr>
<td>Burger</td>
<td>Sandwich</td>
<td colspan="1"></td>
</tr>
</table>