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. ;)
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 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>
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>
I would like to transform an XML document using xslt with a set of row nodes and column nodes into an xhtml table.
The column nodes define data about the associated row attribute. For example, the first column node specifies that the ID attribute of the Row node should be hidden (i.e. not show up in the table). The Caption element of the Column node defines what the column header text should be.
I've seen solutions where you know the attributes that you want to turn into columns ahead of time, but I'm not sure how to use the relevant column data to format the headers
Input:
<TableData>
<Columns>
<Column Name="ID" Hidden="true" />
<Column Name="Name" Caption="Item Name" />
<Column Name="Desc" Caption="Item Description" />
</Columns>
<Rows>
<Row ID="0" Name="A" />
<Row ID="1" Name="B" Desc="Some description"/>
<Row ID="3" Name="C" />
</Rows>
</TableData>
Desired output would be a table in (x)html something like this:
Item Name | Item Description
--------------------------------------
A |
B | Some Description
C |
This transformation:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:key name="RowAttribsByName" match="Row/#*"
use="concat(generate-id(..), '|', name())"/>
<xsl:variable name="vColNames" select=
"/*/Columns/*[not(#Hidden = 'true')]/#Name"/>
<xsl:template match="/*">
<table border="1">
<tr>
<xsl:apply-templates select="Columns/*"/>
</tr>
<xsl:apply-templates select="Rows/Row"/>
</table>
</xsl:template>
<xsl:template match="Column[not(#Hidden = 'true')]">
<td><xsl:value-of select="#Caption"/></td>
</xsl:template>
<xsl:template match="Row">
<tr>
<xsl:apply-templates select="$vColNames">
<xsl:with-param name="pRowId"
select="generate-id()"/>
</xsl:apply-templates>
</tr>
</xsl:template>
<xsl:template match="Column/#*">
<xsl:param name="pRowId"/>
<td width="50%">
<xsl:value-of select=
"key('RowAttribsByName',
concat($pRowId, '|', .)
)
"/>
</td>
</xsl:template>
</xsl:stylesheet>
when applied on the provided XML document:
<TableData>
<Columns>
<Column Name="ID" Hidden="true" />
<Column Name="Name" Caption="Item Name" />
<Column Name="Desc" Caption="Item Description" />
</Columns>
<Rows>
<Row ID="0" Name="A" />
<Row ID="1" Name="B" Desc="Some description"/>
<Row ID="3" Name="C" />
</Rows>
</TableData>
produces the wanted, correct result:
<table border="1">
<tr>
<td>Item Name</td>
<td>Item Description</td>
</tr>
<tr>
<td width="50%">A</td>
<td width="50%"/>
</tr>
<tr>
<td width="50%">B</td>
<td width="50%">Some description</td>
</tr>
<tr>
<td width="50%">C</td>
<td width="50%"/>
</tr>
</table>