Display an XML attribute value only on the first encounter with XSL - xslt

I have this XML fragment:
<svrl:active-pattern document="file:/D:/3.4Workspace/antSchematronTest/xml/CDA.xml"
id="p-2.16.840.1.113883.10.20.1.31-warning"
name="Result observation - warning validation phase"/>
<svrl:fired-rule context="*[cda:templateId/#root="2.16.840.1.113883.10.20.1.31"]"/>
<svrl:failed-assert test="cda:code[#codeSystem='2.16.840.1.113883.6.1' or #codeSystem='2.16.840.1.113883.6.96' ]"
location="/*[local-name()='ClinicalDocument']/*[local-name()='component']/*[local-name()='structuredBody']/*[local-name()='component'][4]/*[local-name()='section']/*[local-name()='entry'][1]/*[local-name()='organizer']/*[local-name()='component'][1]/*[local-name()='observation']">
<svrl:text>The value for "Observation / code" in a result observation SHOULD be selected from LOINC (codeSystem 2.16.840.1.113883.6.1) or SNOMED CT (codeSystem 2.16.840.1.113883.6.96).</svrl:text>
</svrl:failed-assert>
<svrl:failed-assert test="count(cda:effectiveTime)=1"
location="/*[local-name()='ClinicalDocument']/*[local-name()='component']/*[local-name()='structuredBody']/*[local-name()='component'][4]/*[local-name()='section']/*[local-name()='entry'][1]/*[local-name()='organizer']/*[local-name()='component'][1]/*[local-name()='observation']">
<svrl:text>A result observation SHOULD contain exactly one Observation / effectiveTime, which represents the biologically relevant time (e.g. time the specimen was obtained from the patient).</svrl:text>
</svrl:failed-assert>
<svrl:failed-assert test="cda:referenceRange"
location="/*[local-name()='ClinicalDocument']/*[local-name()='component']/*[local-name()='structuredBody']/*[local-name()='component'][4]/*[local-name()='section']/*[local-name()='entry'][1]/*[local-name()='organizer']/*[local-name()='component'][1]/*[local-name()='observation']">
<svrl:text>A result observation SHOULD contain one or more Observation / referenceRange to show the normal range of values for the observation result.</svrl:text>
</svrl:failed-assert>
<svrl:fired-rule context="*[cda:templateId/#root="2.16.840.1.113883.10.20.1.31"]"/>
<svrl:failed-assert test="cda:code[#codeSystem='2.16.840.1.113883.6.1' or #codeSystem='2.16.840.1.113883.6.96' ]"
location="/*[local-name()='ClinicalDocument']/*[local-name()='component']/*[local-name()='structuredBody']/*[local-name()='component'][4]/*[local-name()='section']/*[local-name()='entry'][1]/*[local-name()='organizer']/*[local-name()='component'][2]/*[local-name()='observation']">
<svrl:text>The value for "Observation / code" in a result observation SHOULD be selected from LOINC (codeSystem 2.16.840.1.113883.6.1) or SNOMED CT (codeSystem 2.16.840.1.113883.6.96).</svrl:text>
</svrl:failed-assert>
I'm applying this XSL template to the fragment:
<xsl:template name="svrl:failed-assert" match="svrl:failed-assert">
<p><u>
<xsl:value-of select="(preceding-sibling::svrl:active-pattern)/#name[1]" />
</u></p>
<table width="800">
<tr>
<td colspan="2"><font color="red"><xsl:value-of select="svrl:text" />
</font></td>
</tr>
<tr>
<td width="50">Location:</td>
<td width="750">
<i>
<xsl:call-template name="string-replace-all">
<xsl:with-param name="text">
<xsl:call-template name="string-replace-all">
<xsl:with-param name="text" select="#location"/>
<xsl:with-param name="replace" select=""*[local-name()='""/>
<xsl:with-param name="by" select="''"/>
</xsl:call-template>
</xsl:with-param>
<xsl:with-param name="replace" select=""']""/>
<xsl:with-param name="by" select="''"/>
</xsl:call-template>
</i></td>
</tr>
<tr>
<td width="50">Test:;</td>
<td width="750"><i><xsl:value-of select="#test" /></i></td>
</tr>
</table>
</xsl:template>
<xsl:template name="string-replace-all">
<xsl:param name="text" />
<xsl:param name="replace" />
<xsl:param name="by" />
<xsl:choose>
<xsl:when test="contains($text, $replace)">
<xsl:value-of select="substring-before($text,$replace)" />
<xsl:value-of select="$by" />
<xsl:call-template name="string-replace-all">
<xsl:with-param name="text"
select="substring-after($text,$replace)" />
<xsl:with-param name="replace" select="$replace" />
<xsl:with-param name="by" select="$by" />
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$text" />
</xsl:otherwise>
</xsl:choose>
</xsl:template>
This is the output:
Result observation - warning validation phase
The value for "Observation / code" in a result observation SHOULD be selected from LOINC (codeSystem 2.16.840.1.113883.6.1) or SNOMED CT (codeSystem 2.16.840.1.113883.6.96).
Location: /ClinicalDocument/component/structuredBody/component[4]/section/entry[1]/organizer/component[1]/observation
Test:; cda:code[#codeSystem='2.16.840.1.113883.6.1' or #codeSystem='2.16.840.1.113883.6.96' ]
Result observation - warning validation phase
A result observation SHOULD contain exactly one Observation / effectiveTime, which represents the biologically relevant time (e.g. time the specimen was obtained from the patient).
Location: /ClinicalDocument/component/structuredBody/component[4]/section/entry[1]/organizer/component[1]/observation
Test:; count(cda:effectiveTime)=1
Result observation - warning validation phase
A result observation SHOULD contain one or more Observation / referenceRange to show the normal range of values for the observation result.
Location: /ClinicalDocument/component/structuredBody/component[4]/section/entry[1]/organizer/component[1]/observation
Test:; cda:referenceRange
Result observation - warning validation phase
The value for "Observation / code" in a result observation SHOULD be selected from LOINC (codeSystem 2.16.840.1.113883.6.1) or SNOMED CT (codeSystem 2.16.840.1.113883.6.96).
Location: /ClinicalDocument/component/structuredBody/component[4]/section/entry[1]/organizer/component[2]/observation
Test:; cda:code[#codeSystem='2.16.840.1.113883.6.1' or #codeSystem='2.16.840.1.113883.6.96' ]
I want the svrl:active-pattern#name-“Result observation - warning validation phase” to display only once. For example:
Result observation - warning validation phase
The value for "Observation / code" in a result observation SHOULD be selected from LOINC (codeSystem 2.16.840.1.113883.6.1) or SNOMED CT (codeSystem 2.16.840.1.113883.6.96).
Location: /ClinicalDocument/component/structuredBody/component[4]/section/entry[1]/organizer/component[1]/observation
Test:; cda:code[#codeSystem='2.16.840.1.113883.6.1' or #codeSystem='2.16.840.1.113883.6.96' ]
A result observation SHOULD contain exactly one Observation / effectiveTime, which represents the biologically relevant time (e.g. time the specimen was obtained from the patient).
Location: /ClinicalDocument/component/structuredBody/component[4]/section/entry[1]/organizer/component[1]/observation
Test:; count(cda:effectiveTime)=1
A result observation SHOULD contain one or more Observation / referenceRange to show the normal range of values for the observation result.
Location: /ClinicalDocument/component/structuredBody/component[4]/section/entry[1]/organizer/component[1]/observation
Test:; cda:referenceRange
The value for "Observation / code" in a result observation SHOULD be selected from LOINC (codeSystem 2.16.840.1.113883.6.1) or SNOMED CT (codeSystem 2.16.840.1.113883.6.96).
Location: /ClinicalDocument/component/structuredBody/component[4]/section/entry[1]/organizer/component[2]/observation
Test:; cda:code[#codeSystem='2.16.840.1.113883.6.1' or #codeSystem='2.16.840.1.113883.6.96' ]
A result observation SHOULD contain one or more Observation / referenceRange to show the normal range of values for the observation result.
Location: /ClinicalDocument/component/structuredBody/component[4]/section/entry[1]/organizer/component[2]/observation
Test:; cda:referenceRange
How can I achieve this?

It looks like you only want to output the paragraph for the "Result observation - warning validation phase" message for the first svrl:failed-assert element you find in your XML, but not for any of the others.
What you need to do is have a template to match the first such element, which can output the first paragraph, and then calls the named template svrl:failed-assert which will also match all the other elements too.
<xsl:template match="svrl:failed-assert[1]">
<p>
<u>
<xsl:value-of select="(preceding-sibling::svrl:active-pattern)/#name[1]"/>
</u>
</p>
<xsl:call-template name="svrl:failed-assert"/>
</xsl:template>
<xsl:template match="svrl:failed-assert" name="svrl:failed-assert">
<!-- Existing code here -->
Here is a simplified XSLT (without any find-and-replace code)
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:svrl="oh:no" exclude-result-prefixes="svrl">
<xsl:output method="html" indent="yes"/>
<xsl:template match="svrl:failed-assert[1]">
<p>
<u>
<xsl:value-of select="(preceding-sibling::svrl:active-pattern)/#name[1]"/>
</u>
</p>
<xsl:call-template name="svrl:failed-assert"/>
</xsl:template>
<xsl:template match="svrl:failed-assert" name="svrl:failed-assert">
<table width="800">
<tr>
<td colspan="2">
<font color="red">
<xsl:value-of select="svrl:text"/>
</font>
</td>
</tr>
<tr>
<td width="50">Test:</td>
<td width="750">
<i>
<xsl:value-of select="#test"/>
</i>
</td>
</tr>
</table>
</xsl:template>
</xsl:stylesheet>
When applied to the following simplified XML:
<a xmlns:svrl="oh:no">
<svrl:active-pattern name="Result observation - warning validation phase"/>
<svrl:fired-rule context="Context 1"/>
<svrl:failed-assert test="Test 1" location="Location 1">
<svrl:text>Text 1</svrl:text>
</svrl:failed-assert>
<svrl:failed-assert test="Test 2" location="Location 2">
<svrl:text>Text 2</svrl:text>
</svrl:failed-assert>
<svrl:fired-rule context="Context 2"/>
<svrl:failed-assert test="Test 3" location="Location 3">
<svrl:text>Text 3</svrl:text>
</svrl:failed-assert>
</a>
The following HTML is output:
<p>
<u>Result observation - warning validation phase</u>
</p>
<table width="800">
<tr>
<td colspan="2">
<font color="red">Text 1</font>
</td>
</tr>
<tr>
<td width="50">Test:</td>
<td width="750">
<i>Test 1</i>
</td>
</tr>
</table>
<table width="800">
<tr>
<td colspan="2">
<font color="red">Text 2</font>
</td>
</tr>
<tr>
<td width="50">Test:</td>
<td width="750">
<i>Test 2</i>
</td>
</tr>
</table>
<table width="800">
<tr>
<td colspan="2">
<font color="red">Text 3</font>
</td>
</tr>
<tr>
<td width="50">Test:</td>
<td width="750">
<i>Test 3</i>
</td>
</tr>
</table>

Related

Arithmetic operation does not work when used in with-param in xsl

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.

The variable or parameter 'Rows' is either not defined or it is out of scope

How do i achieve this through xslt?
I am getting an error from the cde above given by you
( sharepoint 2010 xslt dataview : modify table structure to display list item )
The error is: The variable or parameter 'Rows' is either not defined or it is out of scope.
Please help why am i getting this error:(
My full code would go like this after the code by you has been added:
<xsl:template match='dsQueryResponse'>
<table cellpadding="10" cellspacing="0" border="1" style="padding:25px;">
<!--table for head-->
<tr>
<!--table row-->
<td colspan='2'>
<!--table definition-->
<b style="font-size:25px;">ELearning List</b>
<!-- heading-->
</td>
</tr>
<xsl:apply-templates select='Rows/Row'/>
</table>
</xsl:template>
<xsl:template match='Row'>
<!-- template is defined above -->
<xsl:for-each select="$Rows[position() mod 3 = 1]">
<!-- 3 recods in one row should be displayed -->
<tr>
<xsl:variable name="i" select="position() - 1" />
<xsl:for-each select="$Rows[(position() > ($i * 3)) and (position() <= (($i + 1) * 3))]">
<td>
<img src="../PublishingImages/FLDRNEW.GIF" width="50px" height="50px" style="padding-right:20px;"></img>
<!-- image is to display folder below which the hyperlinked text has been populated -->
<br/>
<a href="{#FileRef}" style="font-weight:bold;">
<!-- it is the anchor tag which drives to the corresponding documents -->
<xsl:value-of select="substring-after(string(#FileRef),'/Docs/')"/>
<!-- fetches the value of Name column -->
</a>
</td>
</xsl:for-each>
</tr>
</xsl:for-each>
</xsl:template>
The error is: The variable or parameter 'Rows' is either not defined or it is out of scope.
Please help
Could you give this a try?
<xsl:template match='dsQueryResponse'>
<table cellpadding="10" cellspacing="0" border="1" style="padding:25px;">
<!--table for head-->
<tr>
<!--table row-->
<td colspan='2'>
<!--table definition-->
<b style="font-size:25px;">ELearning List</b>
<!-- heading-->
</td>
</tr>
<xsl:apply-templates
select='Rows/Row[position() mod 3 = 1]' mode="group" />
</table>
</xsl:template>
<xsl:template match='Row' mode="group">
<tr>
<xsl:apply-templates
select=". | following-sibling::Row[position() < 3]" />
</tr>
</xsl:template>
<xsl:template match="Row">
<td>
<!-- image is to display folder below which the hyperlinked text has
been populated -->
<img src="../PublishingImages/FLDRNEW.GIF" width="50px" height="50px"
style="padding-right:20px;" />
<br/>
<!-- the anchor tag which drives to the corresponding documents -->
<a href="{#FileRef}" style="font-weight:bold;">
<!-- fetches the value of Name column -->
<xsl:value-of select="substring-after(string(#FileRef),'/Docs/')"/>
</a>
</td>
</xsl:template>

How to get the sum of values in attributes in xslt

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.

XSL sum() and variable/loop

I have been trying to figure out why my variable assigned sum isn't working, also my table output is repeating only the first element through out the whole table. What i am trying to get this code to do is get the studentID of each student printed in the first column, the student name of that ID in the second column, and then assign a variable that holds the students total mark of the 3 assessments they have completed to print their total mark in the third column, followed by as assignment of HD, D, C, P, or F as based on their total mark e.g. HD is 85 plus and D is 75+ but not higher than 84. etc.
Can someone tell me where I am going wrong? I am still new to XML/XSL so criticism is welcome.
grade.xsl
<?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="/">
<xsl:variable name="StudentAmount" select="count(document('AssessmentItems.xml')/assessmentList/unit/studentList/student)"/>
<xsl:variable name="totalmark" select="sum(document('AssessmentItems.xml')/assessmentList/unit/*
[//assessmentList/unit/assessmentItems/assessment/#studId = //assessmentList/unit/studentList/student/#sid])"/>
<html>
<body>
<h2>Grade Report for <xsl:value-of select="assessmentList/unit/#unitId"/> - <xsl:value-of select="assessmentList/unit/unitName"/></h2>
<p>Number of students in this unit: <xsl:value-of select="$StudentAmount"/></p>
<table border="1">
<tr>
<th>ID</th>
<th>Name</th>
<th>Total Mark</th>
<th>Grade</th>
</tr>
<xsl:for-each select="assessmentList/unit/studentList/student">
<tr>
<td><xsl:value-of select="document('AssessmentItems.xml')/assessmentList/unit/studentList/student/#sid"/></td>
<td><xsl:value-of select="document('AssessmentItems.xml')/assessmentList/unit/studentList/student"/></td>
<td><xsl:value-of select="document('AssessmentItems.xml')/assessmentList/unit/assessmentItems/assessment/mark"/></td>
<xsl:choose>
<xsl:when test="$totalmark > 85">
<td color="blue">HD</td>
</xsl:when>
<xsl:when test="$totalmark > 75">
<td color="black">D</td>
</xsl:when>
<xsl:when test="$totalmark > 65">
<td color="black">C</td>
</xsl:when>
<xsl:when test="$totalmark > 50">
<td color="black">P</td>
</xsl:when>
<xsl:otherwise>
<td color="red">F</td>
</xsl:otherwise>
</xsl:choose>
</tr>
</xsl:for-each>
</table>
</body>
</html>
</xsl:template>
</xsl:stylesheet>
and this is the file AssessmentItems.xml
<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" href="grade.xsl"?>
<assessmentList>
<unit unitId="3311">
<unitName>Learn To Read</unitName>
<studentList>
<student sid="1001">Lisa Simpson</student>
<student sid="1002">Barney Rubble</student>
<student sid="1003">Donald Duck</student>
</studentList>
<assessmentItems>
<assessment name="Assignment 1" weight="20">
<mark studId="1001">12</mark>
<mark studId="1002">18</mark>
<mark studId="1003">9</mark>
</assessment>
<assessment name="Assignment 2" weight="25">
<mark studId="1001">23</mark>
<mark studId="1002">14</mark>
<mark studId="1003">12.5</mark>
</assessment>
<assessment name="Quiz" weight="15">
<mark studId="1001">13</mark>
<mark studId="1002">9</mark>
<mark studId="1003">6</mark>
</assessment>
<assessment name="Final Exam" weight="40">
<mark studId="1001">38</mark>
<mark studId="1002">21</mark>
<mark studId="1003">20.5</mark>
</assessment>
</assessmentItems>
</unit>
</assessmentList>
Firstly, because you are only working on a single XML document, you don't need the constant references to document('AssessmentItems.xml') at all. So, for example
<xsl:value-of
select="document('AssessmentItems.xml')/assessmentList/unit/studentList/student/#sid"/>
Can be replaced with just
<xsl:value-of select="/assessmentList/unit/studentList/student/#sid"/>
This leads on to the second problem. The xpath above is relative to the document element of the XML and will return the #sid of the very first student it finds, and no the #sid of the student you are currently positioned on. You can simply do this in your case
<xsl:value-of select="#sid"/>
Another issue is that you define the variable totalmarks at the top of the XSLT, when in fact it should be defined within the scope of your xsl:for-each so that it is specific for the current student
<xsl:variable name="totalmark" select="sum(../../assessmentItems/assessment/mark[#studId = current()/#sid])" />
Actually, it may be better to make use of a key here, to look up the results
<xsl:key name="marks" match="mark" use="#studId" />
And to get the total results for a student....
<xsl:variable name="totalmark" select="sum(key('marks', #sid))" />
One final comment, although not a problem, it is often better to use xsl:apply-templates rather than xsl:for-each as this avoids excessive indentation, and allows better code re-use.
Try the following XSLT
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:key name="marks" match="mark" use="#studId"/>
<xsl:template match="/">
<xsl:variable name="StudentAmount" select="count(/assessmentList/unit/studentList/student)"/>
<html>
<body>
<h2>Grade Report for
<xsl:value-of select="assessmentList/unit/#unitId"/>-
<xsl:value-of select="assessmentList/unit/unitName"/>
</h2>
<p>Number of students in this unit:
<xsl:value-of select="$StudentAmount"/></p>
<table border="1">
<tr>
<th>ID</th>
<th>Name</th>
<th>Total Mark</th>
<th>Grade</th>
</tr>
<xsl:apply-templates select="assessmentList/unit/studentList/student"/>
</table>
</body>
</html>
</xsl:template>
<xsl:template match="student">
<xsl:variable name="totalmark" select="sum(key('marks', #sid))"/>
<tr>
<td>
<xsl:value-of select="#sid"/>
</td>
<td>
<xsl:value-of select="."/>
</td>
<td>
<xsl:value-of select="$totalmark"/>
</td>
<xsl:choose>
<xsl:when test="$totalmark > 85">
<td color="blue">HD</td>
</xsl:when>
<xsl:when test="$totalmark > 75">
<td color="black">D</td>
</xsl:when>
<xsl:when test="$totalmark > 65">
<td color="black">C</td>
</xsl:when>
<xsl:when test="$totalmark > 50">
<td color="black">P</td>
</xsl:when>
<xsl:otherwise>
<td color="red">F</td>
</xsl:otherwise>
</xsl:choose>
</tr>
</xsl:template>
</xsl:stylesheet>
When applied to your XML, the following HTML is output
<html>
<body>
<h2>Grade Report for 3311- Learn To Read</h2>
<p>Number of students in this unit: 3</p>
<table border="1">
<tr>
<th>ID</th>
<th>Name</th>
<th>Total Mark</th>
<th>Grade</th>
</tr>
<tr>
<td>1001</td>
<td>Lisa Simpson</td>
<td>86</td>
<td color="blue">HD</td>
</tr>
<tr>
<td>1002</td>
<td>Barney Rubble</td>
<td>62</td>
<td color="black">P</td>
</tr>
<tr>
<td>1003</td>
<td>Donald Duck</td>
<td>48</td>
<td color="red">F</td>
</tr>
</table>
</body>
</html>
Do note this assumes only one unit element in your XML. If your actual XML have multiple units, and you wanted a separate table for each, then this is not a problem, you would just need to make sure the unit id is part of the xsl:key so you can look up results for a given student in a given unit.
A very quick glance at your code reveals that the predicate
[//assessmentList/unit/assessmentItems/assessment/#studId = //assessmentList/unit/studentList/student/#sid]
is obviously wrong, because it has the same value (either true or false) for every element in your source document.
Correcting it requires more study of the problem than I have time for. But you seem to have fallen victim to the "if it doesn't work then put '//' at the front" fallacy.

Alternate Row Color of Table using XSLT

I have an XSLT where I want to alternate the row colors of an output table. I know that you can use the code such as in this example:
<table>
<tr>
<td>Name</td>
<td>ID</td>
</tr>
<xsl:for-each select="//Book">
<xsl:variable name="altColor">
<xsl:choose>
<xsl:when test="position() mod 2 = 0">#FFFFFF</xsl:when>
<xsl:otherwise>#D3DFEE</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<tr bgcolor="{$altColor}">
<td><xsl:value-of select="current()/#name"/></td>
<td><xsl:value-of select="current()/#ID"/></td>
</tr>
</xsl:for-each>
</table>
which works fine however, I have a few instances where I need to include some if statements within the for-each, such as.
<table>
<tr>
<td>Name</td>
<td>ID</td>
</tr>
<xsl:for-each select="//Book">
<xsl:variable name="altColor">
<xsl:choose>
<xsl:when test="position() mod 2 = 0">#FFFFFF</xsl:when>
<xsl:otherwise>#D3DFEE</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:if test="current()/#ID > 10000 and current/#ID < 6000">
<tr bgcolor="{$altColor}">
<td><xsl:value-of select="current()/#name"/></td>
<td><xsl:value-of select="current()/#ID"/></td>
</tr>
</xsl:if>
</xsl:for-each>
</table>
Then it doesn't work because it may skip an item at a position in the for-each and I end up with randomly alternating row colors or it may start at an incorrect position where the rows alternate starting with the incorrect color.
I've tried adding an xsl:sort, which doesn't really fix the problem. Is there a way to avoid this snag?
Try with the following code:
tr[position() mod 2 =0]
<xsl:template match="n1:tr[position() mod 2 =0]">
<tr bgcolor="#aaaaaa">
<xsl:apply-templates/>
</tr>
</xsl:template>
<xsl:template match="n1:tr[position() mod 2 =1]">
<tr bgcolor="#aaaaff">
<xsl:apply-templates/>
</tr>
</xsl:template>
The simplest solution (taking your sample stylesheet as representative of your actual needs) is to only loop over the desired nodes in the first place. Like this:
<xsl:template match="/">
<table>
<tr>
<td>Name</td>
<td>ID</td>
</tr>
<xsl:for-each select="//Book[#ID < 10000 and #ID > 6000]">
<xsl:variable name="altColor">
<xsl:choose>
<xsl:when test="position() mod 2 = 0">#FFFFFF</xsl:when>
<xsl:otherwise>#D3DFEE</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<tr bgcolor="{$altColor}">
<td><xsl:value-of select="#name" /></td>
<td><xsl:value-of select="#ID" /></td>
</tr>
</xsl:for-each>
</table>
</xsl:template>
In other words, instead of conditionally including nodes in the body of the for-each simply select only those nodes you need. By doing this, position() will be relative to the set that you're iterating and it will work as expected.
For example, this (simplified) input:
<r>
<Book ID="6200"/>
<Book ID="7100"/>
<Book/>
<Book/>
<Book ID="8000"/>
<Book/>
<Book ID="9001"/>
<Book ID="9002"/>
</r>
Produces the correct alternation:
<table>
<tr>
<td>Name</td>
<td>ID</td>
</tr>
<tr bgcolor="#D3DFEE">
<td />
<td>6200</td>
</tr>
<tr bgcolor="#FFFFFF">
<td />
<td>7100</td>
</tr>
<tr bgcolor="#D3DFEE">
<td />
<td>8000</td>
</tr>
<tr bgcolor="#FFFFFF">
<td />
<td>9001</td>
</tr>
<tr bgcolor="#D3DFEE">
<td />
<td>9002</td>
</tr>
</table>
one simple solution would be to remember last color used and than use opposing one
instead basing your choice on position() and also you should move choice inside test for row
here is pseudo code
currentColor = color1
for each
if ( id > 10000 and id < 6000 ) {
if ( currentColor == color1 )
currentColor= color2
else
currentColor = color1
showDataInColor(currentColor)
}