i would like to know how to get sum of nodes only when they satisfy a condition,having the xml:
<segma>
<conservacio>
<cons estat="A">Excel·lent conservació</cons>
<cons estat="B">Bona conservació</cons>
<cons estat="C">Regular conservació</cons>
<cons estat="D">Mala conservació</cons>
</conservacio>
<categories>
<categoria cat="1">Mobiliari</categoria>
<categoria cat="2">Alimentació</categoria>
<categoria cat="3">Roba</categoria>
</categories>
<clients>
<registre dni="111">Marti</registre>
<registre dni="222">Jana</registre>
<registre dni="333">Edu</registre>
<registre dni="444">Santi</registre>
<registre dni="555">Mia</registre>
<registre dni="666">Pau M</registre>
</clients>
<productes>
<prod id="1" cons="A" cat="1">
<nom>Cuna</nom>
<preu_nou>70</preu_nou>
<preu>30</preu>
<ofertes>
<oferta client="111" />
<oferta client="333" />
</ofertes>
<venta client="111" />
</prod>
<prod id="2" cons="B" cat="2">
<nom>Baby cook</nom>
<preu_nou>120</preu_nou>
<preu>60</preu>
<ofertes>
<oferta client="111" />
<oferta client="333" />
<oferta client="444" />
<oferta client="555" />
</ofertes>
<venta client="555" />
</prod>
<prod id="3" cons="A" cat="1">
<nom>Mama pata</nom>
<preu_nou>70</preu_nou>
<preu>5</preu>
<ofertes>
<oferta client="444" />
<oferta client="555" />
</ofertes>
<venta client="444" />
</prod>
<prod id="4" cons="B" cat="3">
<nom>Conjunt xandall</nom>
<preu_nou>40</preu_nou>
<preu>15</preu>
<ofertes>
<oferta client="222" />
<oferta client="555" />
</ofertes>
<venta client="222" />
</prod>
<prod id="5" cons="C" cat="3">
<nom>Pack 3 texans</nom>
<preu_nou>70</preu_nou>
<preu>25</preu>
<ofertes>
<oferta client="333" />
<oferta client="444" />
<oferta client="555" />
</ofertes>
<venta client="333" />
</prod>
<prod id="6" cons="A" cat="2">
<nom>Saca leches</nom>
<preu_nou>130</preu_nou>
<preu>70</preu>
<ofertes>
<oferta client="111" />
<oferta client="222" />
<oferta client="555" />
</ofertes>
<venta client="111" />
</prod>
<prod id="7" cons="C" cat="2">
<nom>Llet continuació</nom>
<preu_nou>11</preu_nou>
<preu>3</preu>
<ofertes>
</ofertes>
</prod>
</productes>
</segma>
I want to make a list of which products has a client bought(prod/venta/#client), and the sum of prices("preu") of the products:
Having an xsl like :
<h2>Ex 4</h2>
<table border="1">
<xsl:for-each select="//registre">
<xsl:variable name="dni" select="#dni"/>
<tr>
<td> <xsl:value-of select="."/></td>
<xsl:for-each select="//prod">
<xsl:if test="venta/#client=$dni">
<td><xsl:value-of select="nom"/></td>
</xsl:if>
</xsl:for-each>
I would like to get an output like :
client1 prod1 prod2 sum_of_price_prod1+prod2
client2 prod4 prod6 prod 7 sum_of_price_of(prod4+prod6+prod7)
Ex: Maria Baby Cook Texans 744.35
Sorry for the bad english.
You can define a variable to hold all the prod elements current...
<xsl:variable name="prod" select="//prod[venta/#client=$dni]" />
Then you can list select the prod elements like so...
<xsl:for-each select="$prod">
And to get the total sum you can do this...
<xsl:value-of select="sum($prod/preu)" />
Try this XSLT
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="html" doctype-public="XSLT-compat" omit-xml-declaration="yes" encoding="UTF-8" indent="yes" />
<xsl:template match="/">
<h2>Ex 4</h2>
<table border="1">
<xsl:for-each select="//registre">
<xsl:variable name="dni" select="#dni"/>
<xsl:variable name="prod" select="//prod[venta/#client=$dni]" />
<tr>
<td><xsl:value-of select="."/></td>
<td>
<xsl:for-each select="$prod">
<xsl:value-of select="nom"/><br />
</xsl:for-each>
</td>
<td>
<xsl:value-of select="sum($prod/preu)" />
</td>
</tr>
</xsl:for-each>
</table>
</xsl:template>
</xsl:stylesheet>
Alternatively, you could use an xsl:key to look up the prod elements
<xsl:key name="prod" match="prod" use="venta/#client" />
Try this XSLT too
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="html" doctype-public="XSLT-compat" omit-xml-declaration="yes" encoding="UTF-8" indent="yes" />
<xsl:key name="prod" match="prod" use="venta/#client" />
<xsl:template match="/">
<h2>Ex 4</h2>
<table border="1">
<xsl:for-each select="//registre">
<tr>
<td><xsl:value-of select="."/></td>
<td>
<xsl:for-each select="key('prod', #dni)">
<xsl:value-of select="nom"/><br />
</xsl:for-each>
</td>
<td>
<xsl:value-of select="sum(key('prod', #dni)/preu)" />
</td>
</tr>
</xsl:for-each>
</table>
</xsl:template>
</xsl:stylesheet>
Related
I would like to build a table for each JOB of type T. This table must have 2 columns (LPATH and RPATH). LPATH1 must be on the same row as RPATH1, etc...
<ROOT>
<FOLDER>
<JOB type="T">
<VARIABLE NAME="%%FTP-LPATH1" VALUE="/path/to/aL" />
<VARIABLE NAME="%%FTP-RPATH1" VALUE="/path/to/aR" />
<VARIABLE NAME="%%FTP-LPATH2" VALUE="/path/to/bL" />
<VARIABLE NAME="%%FTP-RPATH2" VALUE="/path/to/bR" />
</JOB>
<JOB type="O" />
<JOB type="T">
<VARIABLE NAME="%%FTP-LPATH1" VALUE="/path/to/eL" />
<VARIABLE NAME="%%FTP-RPATH1" VALUE="/path/to/eR" />
</JOB>
</FOLDER>
<FOLDER>
<JOB type="O" />
<JOB type="T">
<VARIABLE NAME="%%FTP-LPATH1" VALUE="/path/to/cL" />
<VARIABLE NAME="%%FTP-RPATH1" VALUE="/path/to/cR" />
<VARIABLE NAME="%%FTP-LPATH2" VALUE="/path/to/dL" />
<VARIABLE NAME="%%FTP-RPATH2" VALUE="/path/to/dR" />
<VARIABLE NAME="%%FTP-LPATH3" VALUE="/path/to/fL" />
<VARIABLE NAME="%%FTP-RPATH3" VALUE="/path/to/fR" />
</JOB>
<JOB type="T">
<VARIABLE NAME="%%FTP-RPATH5" VALUE="/path/to/kR" />
<VARIABLE NAME="%%FTP-LPATH1" VALUE="/path/to/gL" />
<VARIABLE NAME="%%FTP-RPATH1" VALUE="/path/to/gR" />
<VARIABLE NAME="%%FTP-LPATH2" VALUE="/path/to/hL" />
<VARIABLE NAME="%%FTP-RPATH2" VALUE="/path/to/hR" />
<VARIABLE NAME="%%FTP-LPATH3" VALUE="/path/to/iL" />
<VARIABLE NAME="%%FTP-RPATH3" VALUE="/path/to/iR" />
<VARIABLE NAME="%%FTP-LPATH4" VALUE="/path/to/jL" />
<VARIABLE NAME="%%FTP-RPATH4" VALUE="/path/to/jR" />
<VARIABLE NAME="%%FTP-LPATH5" VALUE="/path/to/kL" />
</JOB>
<JOB type="O" />
</FOLDER>
</ROOT>
to obtain something like this:
<table>
<tr>
<th>LPATH</th>
<th>RPATH</th>
</tr>
<tr>
<td>/path/to/aL</td>
<td>/path/to/aR</td>
</tr>
<tr>
<td>/path/to/bL</td>
<td>/path/to/bR</td>
</tr>
...
number of RPATHx/LPATHx couple is not determined
What could you suggest ?
Best regards
If they always come as left/right pairs, you could do simply:
XSLT 1.0
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" omit-xml-declaration="yes" version="1.0" encoding="utf-8" indent="yes"/>
<xsl:template match="/JOB">
<table>
<tr>
<th>LPATH</th>
<th>RPATH</th>
</tr>
<xsl:for-each select="VARIABLE[contains(#NAME, 'LPATH')]">
<tr>
<td>
<xsl:value-of select="#VALUE"/>
</td>
<td>
<xsl:value-of select="following-sibling::VARIABLE[1]/#VALUE"/>
</td>
</tr>
</xsl:for-each>
</table>
</xsl:template>
</xsl:stylesheet>
or even simpler:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" omit-xml-declaration="yes" version="1.0" encoding="utf-8" indent="yes"/>
<xsl:template match="/JOB">
<table>
<tr>
<th>LPATH</th>
<th>RPATH</th>
</tr>
<xsl:for-each select="VARIABLE[position() mod 2 = 1]">
<tr>
<td>
<xsl:value-of select="#VALUE"/>
</td>
<td>
<xsl:value-of select="following-sibling::VARIABLE[1]/#VALUE"/>
</td>
</tr>
</xsl:for-each>
</table>
</xsl:template>
</xsl:stylesheet>
Added:
If they can come out of order, as shown in your updated example, then try:
XSLT 2.0
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" omit-xml-declaration="yes" version="1.0" encoding="utf-8" indent="yes"/>
<xsl:key name="r" match="VARIABLE[starts-with(#NAME, '%%FTP-RPATH')]" use="substring-after(#NAME, '%%FTP-RPATH')" />
<xsl:template match="/ROOT">
<root>
<xsl:for-each select="FOLDER/JOB[#type='T']">
<xsl:variable name="job" select="." />
<table>
<tr>
<th>LPATH</th>
<th>RPATH</th>
</tr>
<xsl:for-each select="VARIABLE[starts-with(#NAME, '%%FTP-LPATH')]">
<tr>
<td>
<xsl:value-of select="#VALUE"/>
</td>
<td>
<xsl:value-of select="key('r', substring-after(#NAME, '%%FTP-LPATH'), $job)/#VALUE"/>
</td>
</tr>
</xsl:for-each>
</table>
</xsl:for-each>
</root>
</xsl:template>
</xsl:stylesheet>
Demo: https://xsltfiddle.liberty-development.net/6pS26mt
Note that this still assumes that they come in pairs - or at least that the LPATH value of the pair will always be present.
Assuming VARIABLE/#NAME always contain LPATHnnn and RPATHnnn as their name :
<xsl:template match="/">
<html>
<table>
<tr>
<th>LPATH</th>
<th>RPATH</th>
</tr>
<xsl:apply-templates select="JOB/VARIABLE[contains(#NAME,'LPATH')]"/>
</table>
</html>
</xsl:template>
<xsl:template match="VARIABLE">
<tr>
<th><xsl:value-of select="#VALUE"/></th>
<xsl:variable name="Rname" select="replace(#NAME,'LPATH','RPATH')"/>
<th><xsl:value-of select="//JOB/VARIABLE[#NAME=$Rname]/#VALUE"/></th>
</tr>
</xsl:template>
See it working here : https://xsltfiddle.liberty-development.net/pNmC4HG
I apply templates with variable in select attribute which contains part of a tree. From that I call another apply templates with following-sibling:: construction, but it applies to all tree. For example:
<a>
<b id="1" ol="1" />
<b id="2" ol="0" />
<b id="3" ol="0" />
<b id="4" ol="1" />
<b id="5" ol="0" />
<b id="6" ol="0" />
<b id="7" ol="1" />
<b id="8" ol="0" />
<b id="9" ol="0" />
<b id="10" ol="1" />
<b id="11" ol="0" />
<b id="12" ol="0" />
<b id="13" ol="1" />
<b id="14" ol="0" />
<b id="15" ol="0" />
<b id="16" ol="1" />
</a>
...
<xsl:variable name="part" select="b[#ol = 1] />
<xsl:apply-templates mode="top" select="$part[position() mod 3 = 1]" />
...
<xsl:template mode="top" match="*">
<tr>
<xsl:apply-template mode="inner" select=".|following-sibling::b[not(position() > 2)]" />
</tr>
<xsl:template>
<xsl:template mode="inner" match="*">
<p><xsl:value-of select="#id" /></p>
<xsl:template>
What I expect is
<tr><p>1</p><p>4</p><p>7</p></tr>
<tr><p>10</p><p>13</p><p>16</p></tr>
What I have got
<tr><p>1</p><p>2</p><p>3</p></tr>
<tr><p>10</p><p>11</p><p>12</p></tr>
So why did template "top" change context to complete tree instead of $part while applying following-sibling? And how to get expected variant?
XPath selects nodes in an input tree, it does never change that input tree. So selecting some nodes does not in any way change the structure and relations in the tree, the sibling or children or ancestors remain the same. If you want to manipulate a tree use XSLT or XQuery. As you already use XSLT, with XSLT 1.0 you would need to write templates to create a result tree fragment with the new structure, then after applying an extension function like exsl:node-set you can process the intermediate tree. With XSLT 2.0 you don't need the extension function but you need to construct an intermediate tree.
To achieve the output you want you could use
<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">
<xsl:output indent="yes" method="html"/>
<xsl:template match="a">
<xsl:variable name="part" select="b[#ol = 1]" />
<xsl:apply-templates mode="top" select="$part[position() mod 3 = 1]" />
</xsl:template>
<xsl:template mode="top" match="*">
<tr>
<xsl:apply-templates mode="inner" select=".|following-sibling::b[#ol = 1][not(position() > 2)]" />
</tr>
</xsl:template>
<xsl:template mode="inner" match="*">
<p><xsl:value-of select="#id" /></p>
</xsl:template>
</xsl:stylesheet>
with that XSLT stylesheet Saxon 6.5.5 transforms
<a>
<b id="1" ol="1" />
<b id="2" ol="0" />
<b id="3" ol="0" />
<b id="4" ol="1" />
<b id="5" ol="0" />
<b id="6" ol="0" />
<b id="7" ol="1" />
<b id="8" ol="0" />
<b id="9" ol="0" />
<b id="10" ol="1" />
<b id="11" ol="0" />
<b id="12" ol="0" />
<b id="13" ol="1" />
<b id="14" ol="0" />
<b id="15" ol="0" />
<b id="16" ol="1" />
</a>
into
<tr>
<p>1</p>
<p>4</p>
<p>7</p>
</tr>
<tr>
<p>10</p>
<p>13</p>
<p>16</p>
</tr>
$part selects elements with #ol=1, namely the elements 1,4,7,10,13,16.
$part[position() mod 3 = 1] selects the items in $part whose position within $part is 1, 4, 7, ... That is, it selects the elements with ids 1 and 10.
You then apply templates to these, to output the groups of three elements starting with these two, which gives you the groups (1,2,3) and (10,11,12).
I think your mistake is probably in imagining that position() returns the position of an element in the tree, rather than the position of the element in the list you are filtering.
I'm fairly new to XSLT but am trying to get an XML file to display a certain node set through XSLT. I am using XSL 1.0. The xml looks like this:
<...>
<entry>
<organizer>
<component>
<observation>
<code displayName="Weight" />
<effectiveTime value="5/21/2013 12:00:00 AM" />
<value value="75" unit="lbs" />
</observation>
</component>
</organizer>
</entry>
<entry>
<organizer>
<component>
<observation>
<code displayName="BMI" />
<effectiveTime value="5/21/2013 12:00:00 AM" />
<value value="14.6" unit="98" />
</observation>
</component>
</organizer>
</entry>
<entry>
<organizer>
<component>
<observation>
<code displayName="Weight" />
<effectiveTime value="5/20/2013 12:00:00 AM" />
<value value="255" unit="lbs" />
</observation>
</component>
</organizer>
</entry>
<entry>
<organizer>
<component>
<observation>
<code displayName="BMI" />
<effectiveTime value="5/20/2013 12:00:00 AM" />
<value value="49.8" unit="98" />
</observation>
</component>
</organizer>
</entry>
<entry>
<organizer>
<component>
<observation>
<code displayName="Blood Pressure" />
<effectiveTime value="5/20/2013 12:00:00 AM" />
<value value="100/76" unit="mm Hg" />
</observation>
</component>
</organizer>
</entry>
</...>
What I want the output to look like is something like this:
<table>
<tr>
<td>5/21/2013</td>
<td> </td>
<td>Weight: 75lbs</td>
<td>BMI: 14.6 90</td>
</tr>
<tr>
<td>5/20/2013</td>
<td>Blood Pressure: 100/76 mm Hg</td>
<td>Weight: 255lbs</td>
<td>BMI: 49.8 90</td>
</tr>
</table>
Basically, group by the effectiveTime (within the observation node), and put the blood pressure, the weight and the bmi in subsequent columns. I also need to have a blank table cell if a particular code displayname is not present for that particular date (see blood pressure not listed for the first date).
Thanks for any help. I'm picking up the XSLT, but it's taking time since there is so much.
You don't say which version of XSLT, so I've assumed 2.0:
T:\ftemp>type entries.xml
<entries>
<entry>
<organizer>
<component>
<observation>
<code displayName="Weight" />
<effectiveTime value="5/21/2013 12:00:00 AM" />
<value value="75" unit="lbs" />
</observation>
</component>
</organizer>
</entry>
<entry>
<organizer>
<component>
<observation>
<code displayName="BMI" />
<effectiveTime value="5/21/2013 12:00:00 AM" />
<value value="14.6" unit="98" />
</observation>
</component>
</organizer>
</entry>
<entry>
<organizer>
<component>
<observation>
<code displayName="Weight" />
<effectiveTime value="5/20/2013 12:00:00 AM" />
<value value="255" unit="lbs" />
</observation>
</component>
</organizer>
</entry>
<entry>
<organizer>
<component>
<observation>
<code displayName="BMI" />
<effectiveTime value="5/20/2013 12:00:00 AM" />
<value value="49.8" unit="98" />
</observation>
</component>
</organizer>
</entry>
<entry>
<organizer>
<component>
<observation>
<code displayName="Blood Pressure" />
<effectiveTime value="5/20/2013 12:00:00 AM" />
<value value="100/76" unit="mm Hg" />
</observation>
</component>
</organizer>
</entry>
</entries>
T:\ftemp>call xslt2 entries.xml entries.xsl
<table>
<tr>
<td>5/21/2013</td>
<td> </td>
<td>Weight: 75 lbs</td>
<td>BMI: 14.6 98</td>
</tr>
<tr>
<td>5/20/2013</td>
<td>Blood Pressure: 100/76 mm Hg</td>
<td>Weight: 255 lbs</td>
<td>BMI: 49.8 98</td>
</tr>
</table>
T:\ftemp>type entries.xsl
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="2.0">
<xsl:output method="html"/>
<xsl:template match="entries">
<!--dictate the order of the columns this way-->
<xsl:variable name="fields" select="('Blood Pressure','Weight','BMI')"/>
<!--create the table-->
<table>
<!--grouped by time-->
<xsl:for-each-group select="entry"
group-by="organizer/component/observation/effectiveTime/#value">
<tr>
<!--subset of time-->
<td>
<xsl:value-of select="substring-before(
organizer/component/observation/effectiveTime/#value,' ')"/>
</td>
<!--the fields in order-->
<xsl:for-each select="$fields">
<xsl:variable name="this"
select="current-group()[organizer/component/observation/code/#displayName=
current()]/organizer/component/observation/value"/>
<xsl:choose>
<xsl:when test="$this">
<td>
<xsl:value-of
select="concat(.,': ',$this/#value,' ',$this/#unit)"/>
</td>
</xsl:when>
<xsl:otherwise><td> </td></xsl:otherwise>
</xsl:choose>
</xsl:for-each>
</tr>
</xsl:for-each-group>
</table>
</xsl:template>
</xsl:stylesheet>
T:\ftemp>rem Done!
Here is a version of solution using XSLT 1.0 ... rather than using a variable of field names, I coded the column handling for each one.
T:\ftemp>type entries.xml
<entries>
<entry>
<organizer>
<component>
<observation>
<code displayName="Weight" />
<effectiveTime value="5/21/2013 12:00:00 AM" />
<value value="75" unit="lbs" />
</observation>
</component>
</organizer>
</entry>
<entry>
<organizer>
<component>
<observation>
<code displayName="BMI" />
<effectiveTime value="5/21/2013 12:00:00 AM" />
<value value="14.6" unit="98" />
</observation>
</component>
</organizer>
</entry>
<entry>
<organizer>
<component>
<observation>
<code displayName="Weight" />
<effectiveTime value="5/20/2013 12:00:00 AM" />
<value value="255" unit="lbs" />
</observation>
</component>
</organizer>
</entry>
<entry>
<organizer>
<component>
<observation>
<code displayName="BMI" />
<effectiveTime value="5/20/2013 12:00:00 AM" />
<value value="49.8" unit="98" />
</observation>
</component>
</organizer>
</entry>
<entry>
<organizer>
<component>
<observation>
<code displayName="Blood Pressure" />
<effectiveTime value="5/20/2013 12:00:00 AM" />
<value value="100/76" unit="mm Hg" />
</observation>
</component>
</organizer>
</entry>
</entries>
T:\ftemp>call xslt entries.xml entries1.xsl
<table>
<tr>
<td>5/21/2013</td>
<td> </td>
<td>Weight: 75 lbs</td>
<td>BMI: 14.6 98</td>
</tr>
<tr>
<td>5/20/2013</td>
<td>Blood Pressure: 100/76 mm Hg</td>
<td>Weight: 255 lbs</td>
<td>BMI: 49.8 98</td>
</tr>
</table>
T:\ftemp>type entries1.xsl
<?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:key name="times" match="entry"
use="organizer/component/observation/effectiveTime/#value"/>
<xsl:template match="entries">
<!--create the table-->
<table>
<!--grouped by time-->
<xsl:for-each select="entry[generate-id(.)=
generate-id(key('times',
organizer/component/observation/effectiveTime/#value)[1])]">
<tr>
<!--subset of time-->
<td>
<xsl:value-of select="substring-before(
organizer/component/observation/effectiveTime/#value,' ')"/>
</td>
<!--the fields in order-->
<xsl:variable name="values"
select="key('times',
organizer/component/observation/effectiveTime/#value)/
organizer/component/observation"/>
<xsl:choose>
<xsl:when test="$values/code[#displayName='Blood Pressure']">
<td>
<xsl:for-each
select="$values[code/#displayName='Blood Pressure']">
<xsl:value-of
select="concat('Blood Pressure: ',value/#value,' ',value/#unit)"/>
</xsl:for-each>
</td>
</xsl:when>
<xsl:otherwise><td> </td></xsl:otherwise>
</xsl:choose>
<xsl:choose>
<xsl:when test="$values/code[#displayName='Weight']">
<td>
<xsl:for-each
select="$values[code/#displayName='Weight']">
<xsl:value-of
select="concat('Weight: ',value/#value,' ',value/#unit)"/>
</xsl:for-each>
</td>
</xsl:when>
<xsl:otherwise><td> </td></xsl:otherwise>
</xsl:choose>
<xsl:choose>
<xsl:when test="$values/code[#displayName='BMI']">
<td>
<xsl:for-each
select="$values[code/#displayName='BMI']">
<xsl:value-of
select="concat('BMI: ',value/#value,' ',value/#unit)"/>
</xsl:for-each>
</td>
</xsl:when>
<xsl:otherwise><td> </td></xsl:otherwise>
</xsl:choose>
</tr>
</xsl:for-each>
</table>
</xsl:template>
</xsl:stylesheet>
T:\ftemp>rem Done!
<xsl:stylesheet
version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
>
<xsl:output method="html" indent="yes" />
<xsl:key name="kObservationByTime" match="observation" use="effectiveTime/#value" />
<xsl:template match="/">
<table>
<tr>
<th>Time</th>
<th>Blood Pressure</th>
<th>Weight</th>
<th>BMI</th>
</tr>
<xsl:apply-templates mode="group" select="
*//observation[
generate-id()
=
generate-id(key('kObservationByTime', effectiveTime/#value)[1])
][position() > last() - 3]
" />
<xsl:sort select="effectiveTime/#value" order="descending" />
</xsl:apply-templates>
</table>
</xsl:template>
<xsl:template match="observation" mode="group">
<xsl:variable name="time" select="effectiveTime/#value" />
<xsl:variable name="thisGroup" select="key('kObservationByTime', $time)" />
<tr>
<td><xsl:value-of select="substring-before($time, ' ')" /></td>
<td><xsl:apply-templates select="$thisGroup[code[#displayName = 'Blood Pressure']]" /></td>
<td><xsl:apply-templates select="$thisGroup[code[#displayName = 'Weight']]" /></td>
<td><xsl:apply-templates select="$thisGroup[code[#displayName = 'BMI']]" /></td>
</tr>
</xsl:template>
<xsl:template match="observation">
<xsl:value-of select="normalize-space(concat(value/#value, ' ', value/#unit))" />
</xsl:template>
</xsl:stylesheet>
gives you
<table>
<tr>
<th>Time</th>
<th>Blood Pressure</th>
<th>Weight</th>
<th>BMI</th>
</tr>
<tr>
<td>5/21/2013</td>
<td></td>
<td>75 lbs</td>
<td>14.6 98</td>
</tr>
<tr>
<td>5/20/2013</td>
<td>100/76 mm Hg</td>
<td>255 lbs</td>
<td>49.8 98</td>
</tr>
</table>
http://www.xmlplayground.com/O9Z9DT
This solution uses an <xsl:key> for grouping.
The [position() > last() - 3] predicate selects the last 2 groups for display. The alternative, [position() < 3], would select the first two. Both work in document order, while <xsl:sort> only affects output order.
Note that due to an unwise choice in date formatting, ordering your results sensibly isn't possible with pretty code.
The non-pretty code to sort your entries by date would look like this:
<xsl:sort select="
concat(
substring-after(substring-after(substring-before(effectiveTime/#value, ' '), '/'), '/'),
'-',
10 + substring-before(effectiveTime/#value, '/'),
'-',
10 + substring-before(substring-after(effectiveTime/#value, '/'), '/')
)
" order="descending" />
... or you simply use a sensible date format and the pain goes away. ;)
With XSLT, how can I change the following:
<root>
<element id="1" Team="Rangers" Season="2011" Points="12" />
<element id="2" Team="Rangers" Season="2012" Points="5" />
<element id="3" Team="Rangers" Season="2012" Points="3" />
<element id="4" Team="Astros" Season="2011" Points="12" />
<element id="5" Team="Astros" Season="2011" Points="9" />
<element id="5" Team="Astros" Season="2012" Points="2" />
</root>
Into:
<body>
<h2>Rangers</h2>
<table>
<tr><td>2011</td><td>12</td></tr>
<tr><td>2012</td><td>8</td></tr>
<tr><td>Total</td><td>20</td></tr>
<h2>Astros</h2>
<table>
<tr><td>2011</td><td>21</td></tr>
<tr><td>2012</td><td>2</td></tr>
<tr><td>Total</td><td>1227707</td></tr>
</table>
</body>
I tried this example but it is missing the extra Muenchian grouping required.
Sums and Category Grouping with XSLT
Any help would be greatly appreciated!
This should do it:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes" />
<xsl:key name="kTeam" match="element" use="#Team" />
<xsl:key name="kTeamYear" match="element" use="concat(#Team, '+', #Season)"/>
<xsl:template match="root">
<body>
<xsl:apply-templates select="element[generate-id(.) =
generate-id(key('kTeam',#Team)[1])]"
mode="group"/>
</body>
</xsl:template>
<xsl:template match="element" mode="group">
<xsl:variable name="thisTeam" select="key('kTeam',#Team)" />
<h2>
<xsl:value-of select="#Team" />
</h2>
<table>
<xsl:apply-templates
select="$thisTeam[generate-id() =
generate-id(key('kTeamYear',
concat(#Team, '+', #Season))[1]
)]" />
<tr>
<td>
<xsl:text>Total</xsl:text>
</td>
<td>
<xsl:value-of select="sum($thisTeam/#Points)"/>
</td>
</tr>
</table>
</xsl:template>
<xsl:template match="element">
<xsl:variable name="thisSeason"
select="key('kTeamYear', concat(#Team, '+', #Season))" />
<tr>
<td>
<xsl:value-of select="#Season" />
</td>
<td>
<xsl:value-of select="sum($thisSeason/#Points)" />
</td>
</tr>
</xsl:template>
</xsl:stylesheet>
When run on your sample input, this produces:
<body>
<h2>Rangers</h2>
<table>
<tr>
<td>2011</td>
<td>12</td>
</tr>
<tr>
<td>2012</td>
<td>8</td>
</tr>
<tr>
<td>Total</td>
<td>20</td>
</tr>
</table>
<h2>Astros</h2>
<table>
<tr>
<td>2011</td>
<td>21</td>
</tr>
<tr>
<td>2012</td>
<td>2</td>
</tr>
<tr>
<td>Total</td>
<td>23</td>
</tr>
</table>
</body>
This question already has an answer here:
xslt Format data in a table some columns are blank
(1 answer)
Closed 9 years ago.
With XSLT 1.0, how can I change the following:
<root>
<element id="1" Team="Rangers" Season="2011" Points="12" />
<element id="2" Team="Rangers" Season="2012" Points="5" />
<element id="3" Team="Rangers" Season="2012" Points="4" />
<element id="4" Team="Rangers" Season="2013" Points="3" />
<element id="5" Team="Astros" Season="2011" Points="12" />
<element id="6" Team="Astros" Season="2013" Points="2" />
<element id="7" Team="Astros" Season="2013" Points="1" />
<element id="8" Team="Astros" Season="2013" Points="2" />
</root>
Into:
<table>
<tr><td>Team</td>
<td>2011</td>
<td>2012</td>
<td>2013</td>
<td>Total</td></tr>
<tr><td>Rangers</td>
<td>12</td>
<td>9</td>
<td>3</td>
<td>20</td></tr>
<tr><td>Astros</td>
<td>12</td>
<td>0</td>
<td>5</td>
<td>14</td></tr>
</table>
I asked a similar question here but missed an important detail.
xslt Format data in a table some columns are blank
Notice that the sub groupings also has a summary.
Any help would be greatly appreciated!
I'm not entirely sure how this question is different from your last one, but this should do it:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output indent="yes" omit-xml-declaration="yes"/>
<xsl:key name="kTeam" match="element/#Team" use="."/>
<xsl:key name="kSeason" match="element/#Season" use="."/>
<xsl:variable name="distinctSeasons"
select="/root/element/#Season
[generate-id() = generate-id(key('kSeason', .)[1])]" />
<xsl:template match="root">
<table>
<tbody>
<tr>
<td>Team</td>
<xsl:apply-templates select="$distinctSeasons" mode="header">
<xsl:sort select="." data-type="number" />
</xsl:apply-templates>
<td>Total</td>
</tr>
<xsl:apply-templates
select="element/#Team[generate-id() =
generate-id(key('kTeam', .)[1])]" />
</tbody>
</table>
</xsl:template>
<xsl:template match="#Team">
<xsl:variable name="thisTeamRows" select="key('kTeam', .)/.." />
<tr>
<td>
<xsl:value-of select="." />
</td>
<xsl:apply-templates select="$distinctSeasons" mode="total">
<xsl:sort select="." data-type="number" />
<xsl:with-param name="teamRows" select="$thisTeamRows" />
</xsl:apply-templates>
<td>
<xsl:value-of select="sum($thisTeamRows/#Points)" />
</td>
</tr>
</xsl:template>
<xsl:template match="#Season" mode="header">
<td>
<xsl:value-of select="." />
</td>
</xsl:template>
<xsl:template match="#Season" mode="total">
<xsl:param name="teamRows" />
<td>
<xsl:value-of select="sum($teamRows[#Season = current()]/#Points)"/>
</td>
</xsl:template>
</xsl:stylesheet>
When run on your sample input, this produces:
<table>
<tbody>
<tr>
<td>Team</td>
<td>2011</td>
<td>2012</td>
<td>2013</td>
<td>Total</td>
</tr>
<tr>
<td>Rangers</td>
<td>12</td>
<td>9</td>
<td>3</td>
<td>24</td>
</tr>
<tr>
<td>Astros</td>
<td>12</td>
<td>0</td>
<td>5</td>
<td>17</td>
</tr>
</tbody>
</table>