How can I transform this XML:
<data>
<entry a="2" b="3" />
<entry a="2" c="3" />
<entry b="2" c="3" />
<entry a="1" b="2" c="3" />
</data>
Into a table containing a union of all the attributes of the entrys in the header, with either the values or blanks in the rows:
<table>
<tr><th>a</th><th>b</th><th>c</th></tr>
<tr><td>2</td><td>3</td><td> </td></tr>
<tr><td>2</td><td> </td><td>3</td></tr>
<tr><td> </td><td>2</td><td>3</td></tr>
<tr><td>1</td><td>2</td><td>3</td></tr>
</table>
<xsl:key name="attrs" match="data/entry/#*" use="local-name(.)"/>
<xsl:variable name="unique-attr-names" select="data/entry/#*[generate-id() = generate-id(key('attrs', local-name(.))[1])]" />
<xsl:template match="data">
<table>
<tr><xsl:call-template name="unique-attrs" /></tr>
<xsl:apply-templates />
</table>
</xsl:template>
<xsl:template name="unique-attrs">
<xsl:for-each select="$unique-attr-names">
<xsl:sort data-type="text" order="ascending" />
<th><xsl:value-of select="local-name(.)"/></th>
</xsl:for-each>
</xsl:template>
<xsl:template match="entry">
<xsl:variable name="e" select="." />
<tr>
<xsl:for-each select="$unique-attr-names">
<xsl:sort data-type="text" order="ascending" />
<xsl:variable name="a" select="." />
<th><xsl:value-of select="($e/#*[local-name() = local-name($a)])[1]" /></th>
</xsl:for-each>
</tr>
</xsl:template>
You may also try this (based on muenchian grouping):
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" >
<xsl:output method="xml" />
<xsl:key name="kEntryAttr" match="entry/#*" use="name()" />
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="entry">
<xsl:variable name="e" select="." />
<tr>
<xsl:for-each select="//entry/#*[count(. | key('kEntryAttr', name() )[1] ) = 1]" >
<td>
<xsl:value-of select="$e/#*[name() = name(current() )]"/>
</td>
</xsl:for-each>
</tr>
</xsl:template>
<xsl:template match="*">
<table>
<tr>
<xsl:for-each select="//entry/#*[count(. | key('kEntryAttr', name() )[1] ) = 1]" >
<th>
<xsl:value-of select="name()"/>
</th>
</xsl:for-each>
</tr>
<xsl:apply-templates select="entry" />
</table>
</xsl:template>
</xsl:stylesheet>
Which will generate the following output.
<?xml version="1.0"?>
<table>
<tr>
<th>a</th>
<th>b</th>
<th>c</th>
</tr>
<tr>
<td>2</td>
<td>3</td>
<td/>
</tr>
<tr>
<td>2</td>
<td/>
<td>3</td>
</tr>
<tr>
<td/>
<td>2</td>
<td>3</td>
</tr>
<tr>
<td>1</td>
<td>2</td>
<td>3</td>
</tr>
</table>
Related
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>
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="2013" Points="3" />
<element id="4" Team="Astros" Season="2011" Points="12" />
<element id="5" 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>5</td>
<td>3</td>
<td>20</td></tr>
<tr><td>Astros</td>
<td>12</td>
<td>0</td>
<td>2</td>
<td>14</td></tr>
</table>
The heading can be generated manually as it is static, but I am unsure how to handle null data.
Any help would be greatly appreciated!
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output indent="yes"/>
<xsl:key name="T" match="element" use="#Team"/>
<xsl:key name="S" match="element" use="#Season"/>
<xsl:template match="root">
<xsl:variable name="years" select="element[generate-id()=generate-id(key('S',#Season)[1])]"/>
<table>
<tbody>
<tr>
<td>Team</td>
<xsl:for-each select="$years">
<xsl:sort select="#Season"/>
<td><xsl:value-of select="#Season"/></td>
</xsl:for-each>
<td>Total</td>
</tr>
<xsl:for-each select="element[generate-id()=generate-id(key('T',#Team)[1])]">
<tr>
<xsl:variable name="thisteam" select="key('T',#Team)"/>
<xsl:for-each select="$years">
<xsl:sort select="#Season"/>
<td>
<xsl:choose>
<xsl:when test="$thisteam[#Season=current()/#Season]">
<xsl:value-of select="$thisteam[#Season=current()/#Season]/#Points"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="0"/>
</xsl:otherwise>
</xsl:choose>
</td>
</xsl:for-each>
<td><xsl:value-of select="sum($thisteam/#Points)"/></td>
</tr>
</xsl:for-each>
</tbody>
</table>
</xsl:template>
</xsl:stylesheet>
Produces
<?xml version="1.0" encoding="utf-8"?>
<table>
<tbody>
<tr>
<td>Team</td>
<td>2011</td>
<td>2012</td>
<td>2013</td>
<td>Total</td>
</tr>
<tr>
<td>12</td>
<td>5</td>
<td>3</td>
<td>20</td>
</tr>
<tr>
<td>12</td>
<td>0</td>
<td>2</td>
<td>14</td>
</tr>
</tbody>
</table>
How to delete an empty column in HTML table using XSLT, and having something like this:
<table id="cas6">
<tr>
<td />
<td>
<table>
<tr>
<td>rechin</td>
<td />
</tr>
<tr>
<td>amarillo</td>
<td />
</tr>
</table>
</td>
</tr>
</table>
<table id="cas7">
<tr>
<td>rechin</td>
<td />
</tr>
<tr>
<td>amarillo</td>
<td />
</tr>
<tr>
<td>this shouldn't been</td>
<td>deleted</td>
</tr>
</table>
To delete the empty column, this being said to remove td's which are empty in all tr's in a Xth position
Here is a very simple solution:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:template match="node()|#*" name="identity">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="td[not(node())]">
<xsl:variable name="vPos" select="position()"/>
<xsl:if test="../../tr/td[position() = $vPos]/node()">
<xsl:copy-of select="."/>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
When this transformation is applied on the provided XML document (made well-formed):
<html>
<table border="1" id="cas6">
<tr>
<td/>
<td>
<table border="1">
<tr>
<td>rechin</td>
<td />
</tr>
<tr>
<td>amarillo</td>
<td />
</tr>
</table></td>
</tr>
</table>
<table border="1" id="cas7">
<tr>
<td>rechin</td>
<td />
</tr>
<tr>
<td>amarillo</td>
<td />
</tr>
<tr>
<td>this shouldn't been</td>
<td>deleted</td>
</tr>
</table>
</html>
The wanted correct result is produced:
<html>
<table border="1" id="cas6">
<tr>
<td>
<table border="1">
<tr>
<td>rechin</td>
</tr>
<tr>
<td>amarillo</td>
</tr>
</table></td>
</tr>
</table>
<table border="1" id="cas7">
<tr>
<td>rechin</td>
<td></td>
</tr>
<tr>
<td>amarillo</td>
<td></td>
</tr>
<tr>
<td>this shouldn't been</td>
<td>deleted</td>
</tr>
</table>
</html>
This is the XSLT that worked for me.
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*" />
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="td[not(node())]">
<xsl:variable name="pos" select="position()" />
<xsl:variable name="emptyTds" select="count(../../tr/td[position() = $pos and not(node())])" />
<xsl:variable name="allTds" select="count(../../tr/td[position() = $pos])" />
<xsl:if test="$emptyTds != $allTds">
<xsl:copy>
<xsl:value-of select="."/>
</xsl:copy>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
If my understanding of the question is correct, for a given table, if all entries in the nth column are empty, you want to delete that column from the table?
Try this XSLT then
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="td">
<xsl:variable name="columnNumber" select="position()"/>
<xsl:if test="../../tr/td[position()=$columnNumber][* or text()]">
<xsl:copy>
<xsl:value-of select="$columnNumber"/>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:if>
</xsl:template>
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
This is the 'identity' transform, but when it matches a TD element, it first gets the column number, and then checks if any other column in other rows are empty. If it finds any non-empty cells in the same column, it copies the TD element, otherwise it is ignored.
Could anyone help me with the following transformation?
Here is the XML
<Chart>
<Chart.Series>
<DataSeries LegendText="Complete On Time" >
<DataSeries.DataPoints>
<DataPoint AxisXLabel="Sep 09" YValue="10" />
<DataPoint AxisXLabel="Oct 09" YValue="11" />
<DataPoint AxisXLabel="Nov 09" YValue="12" />
</DataSeries.DataPoints>
</DataSeries>
<DataSeries LegendText="Complete Overdue" >
<DataSeries.DataPoints>
<DataPoint YValue="1" />
<DataPoint YValue="2" />
<DataPoint YValue="3" />
</DataSeries.DataPoints>
</DataSeries>
</Chart.Series>
</Chart>
and here is the output id like
<table>
<thead>
<tr>
<th></th>
<th>Complete On Time</th>
<th>Complete Overdue</th>
</tr>
</thead>
<tbody>
<tr>
<th>Sep 09</th>
<th>10</th>
<th>1</th>
</tr>
<tr>
<th>Oct 09</th>
<th>11</th>
<th>2</th>
</tr>
<tr>
<th>Nov 09</th>
<th>12</th>
<th>3</th>
</tr>
</tbody>
A more natural solution:
<xsl:stylesheet
version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
>
<!-- basic table structure -->
<xsl:template match="Chart">
<table>
<thead>
<xsl:apply-templates select="Chart.Series" mode="thead" />
</thead>
<tbody>
<xsl:apply-templates select="Chart.Series" mode="tbody" />
</tbody>
</table>
</xsl:template>
<!-- table head -->
<xsl:template match="Chart.Series" mode="thead">
<tr>
<th />
<xsl:for-each select="DataSeries">
<th>
<xsl:value-of select="#LegendText" />
</th>
</xsl:for-each>
</tr>
</xsl:template>
<!-- table body -->
<xsl:template match="Chart.Series" mode="tbody">
<xsl:variable name="ds" select="DataSeries" />
<!-- the first data series contains the labels -->
<xsl:for-each select="$ds[1]/*/DataPoint">
<xsl:variable name="pos" select="position()" />
<tr>
<td>
<xsl:value-of select="#AxisXLabel" />
</td>
<!-- process all data points at the current position -->
<xsl:apply-templates select="$ds/*/DataPoint[$pos]" />
</tr>
</xsl:for-each>
</xsl:template>
<!-- data points become a <td> -->
<xsl:template match="DataPoint">
<td>
<xsl:value-of select="#YValue" />
</td>
</xsl:template>
</xsl:stylesheet>
Note that I use template modes to do different things with the same input.
The result is:
<table>
<thead>
<tr>
<th />
<th>Complete On Time</th>
<th>Complete Overdue</th>
</tr>
</thead>
<tbody>
<tr>
<td>Sep 09</td>
<td>10</td>
<td>1</td>
</tr>
<tr>
<td>Oct 09</td>
<td>11</td>
<td>2</td>
</tr>
<tr>
<td>Nov 09</td>
<td>12</td>
<td>3</td>
</tr>
</tbody>
</table>
This is something close. It's been a couple of year for me though that I've done XSLT - ignore bad style.
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" >
<xsl:output indent="yes" omit-xml-declaration="yes" ></xsl:output>
<xsl:template match="/">
<table>
<thead>
<tr>
<th></th>
<xsl:apply-templates select=".//DataSeries" />
</tr>
</thead>
<tbody>
<xsl:apply-templates select="//DataSeries[1]//DataPoint">
<xsl:with-param name="datablock">1</xsl:with-param>
</xsl:apply-templates>
</tbody>
</table>
</xsl:template>
<xsl:template match="DataSeries">
<th>
<xsl:value-of select="#LegendText"></xsl:value-of>
</th>
</xsl:template>
<xsl:template match="DataPoint">
<xsl:param name="datablock" />
<xsl:variable name="posi" select="position()"></xsl:variable>
<xsl:if test="$datablock = 1">
<tr>
<xsl:for-each select="#*">
<th>
<xsl:value-of select="."></xsl:value-of>
</th>
</xsl:for-each>
<xsl:apply-templates select="//DataSeries[$datablock+1]//DataPoint[$posi]">
<xsl:with-param name="datablock" select="$datablock + 1">
</xsl:with-param>
</xsl:apply-templates>
</tr>
</xsl:if>
<xsl:if test="$datablock != 1">
<xsl:for-each select="#*">
<th>
<xsl:value-of select="."></xsl:value-of>
</th>
</xsl:for-each>
<xsl:apply-templates select="//DataSeries[$datablock+1]//DataPoint[$posi]">
<xsl:with-param name="datablock" select="$datablock + 1">
</xsl:with-param>
</xsl:apply-templates>
</xsl:if>
</xsl:template>
I'm trying to go from this kind of input:
<col title="one">
<cell>a</cell> <cell>b</cell> <cell>c</cell> <cell>d</cell>
</col>
<col title="two">
<cell>e</cell> <cell>f</cell> <cell>g</cell>
</col>
... to this HTML output with XSLT:
<table>
<tr> <th>one</th> <th>two</th> </tr>
<tr> <td>a</td> <td>e</td> </tr>
<tr> <td>b</td> <td>f</td> </tr>
<tr> <td>c</td> <td>g</td> </tr>
<tr> <td>d</td> </tr>
</table>
In other words I want to perform a matrix transposition. I couldn't find a simple way to do that, there probably isn't, I guess; how about a complicated one? While searching on Google I found hints that a way to solve this was through recursion. Any idea appreciated.
One possibility is to find the <col> with the most cells and then iterate over them in a nested loop. This guarantees the generation of a structurally valid HTML table.
<!-- this variable stores the unique ID of the longest <col> -->
<xsl:variable name="vMaxColId">
<xsl:for-each select="/root/col">
<xsl:sort select="count(cell)" data-type="number" order="descending" />
<xsl:if test="position() = 1">
<xsl:value-of select="generate-id()" />
</xsl:if>
</xsl:for-each>
</xsl:variable>
<!-- and this selects the children of that <col> for later iteration -->
<xsl:variable name="vIter" select="
/root/col[generate-id() = $vMaxColId]/cell
" />
<xsl:template match="root">
<xsl:variable name="columns" select="col" />
<table>
<!-- output the <th>s -->
<tr>
<xsl:apply-templates select="$columns/#title" />
</tr>
<!-- make as many <tr>s as there are <cell>s in the longest <col> -->
<xsl:for-each select="$vIter">
<xsl:variable name="pos" select="position()" />
<tr>
<!-- make as many <td>s as there are <col>s -->
<xsl:for-each select="$columns">
<td>
<xsl:value-of select="cell[position() = $pos]" />
</td>
</xsl:for-each>
</tr>
</xsl:for-each>
</table>
</xsl:template>
<xsl:template match="col/#title">
<th>
<xsl:value-of select="." />
</th>
</xsl:template>
Applied to
<root>
<col title="one">
<cell>a</cell> <cell>b</cell> <cell>c</cell> <cell>d</cell>
</col>
<col title="two">
<cell>e</cell> <cell>f</cell> <cell>g</cell>
</col>
</root>
this produces:
<table>
<tr>
<th>one</th> <th>two</th>
</tr>
<tr>
<td>a</td> <td>e</td>
</tr>
<tr>
<td>b</td> <td>f</td>
</tr>
<tr>
<td>c</td> <td>g</td>
</tr>
<tr>
<td>d</td> <td></td>
</tr>
</table>
From Marrow:
<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="html" indent="yes"/>
<xsl:template match="input">
<table border="1">
<xsl:apply-templates select="col[1]/cell"/>
</table>
</xsl:template>
<xsl:template match="cell">
<xsl:variable name="curr-pos" select="position()"/>
<tr>
<td>
<xsl:copy-of select="node()|../following-sibling::col/cell[$curr-pos]/node()"/>
</td>
</tr>
</xsl:template>
</xsl:stylesheet>
I put input tags around your xml to make it closer match an example I found.
(getting closer).
BTW: you can test by adding this as your 2nd line to your xml:
<?xml-stylesheet type="text/xsl" href="NonLinear.xslt"?>