Reputation: 1262
I have an XML document:
<Gym>
<Trainee>
<Trainee_ID>1521</Trainee_ID>
<Trainee_Name>Mary Andersen</Trainee_Name>
<Trainee_Age>23</Trainee_Age>
</Trainee>
<Trainee>
<Trainee_ID>1522</Trainee_ID>
<Trainee_Name>Jane Sellers</Trainee_Name>
<Trainee_Age>56</Trainee_Age>
</Trainee>
<Trainee>
<Trainee_ID>1523</Trainee_ID>
<Trainee_Name>Julie Aniston</Trainee_Name>
<Trainee_Age>32</Trainee_Age>
</Trainee>
<Class>
<Trainee_ID>1521</Trainee_ID>
<Course_ID>A21</Course_ID>
<Class_Room>A1</Class_Room>
</Class>
<Class>
<Trainee_ID>1522</Trainee_ID>
<Course_ID>A22</Course_ID>
<Class_Room>B2</Class_Room>
</Class>
<Class>
<Trainee_ID>1523</Trainee_ID>
<Course_ID>B24</Course_ID>
<Class_Room>B3</Class_Room>
</Class>
<Course>
<Course_ID>A21</Course_ID>
<Course_Title>Yoga</Course_Title>
<Course_Hours_Per_Week>2</Course_Hours_Per_Week>
</Course>
<Course>
<Course_ID>A22</Course_ID>
<Course_Title>Pilates</Course_Title>
<Course_Hours_Per_Week>2</Course_Hours_Per_Week>
</Course>
<Course>
<Course_ID>B24</Course_ID>
<Course_Title>Aerobic</Course_Title>
<Course_Hours_Per_Week>3</Course_Hours_Per_Week>
</Course>
</Gym>
What I want to do is make 3 tables with XSL, with header the name of the children element (only once), sub-headers the name of the grandchildren elements (only once) and the rest rows and columns will be filled with the values of the grandchildren elements.
This is as far as I got:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/Gym">
<html>
<body>
<xsl:for-each select="*">
<table border="1">
<tr>
<xsl:if test="position()='1' or position()='4' or position()='7'">
<xsl:value-of select="translate(name(.), 'abcdefghijklnmopqrstuvwxyz', 'ABCDEFGHIJKLNMOPQRSTUVWXYZ')"/>
</xsl:if>
</tr>
<tr>
<xsl:if test="position()='1' or position()='4' or position()='7'">
<xsl:for-each select="*">
<th><xsl:value-of select="name(.)"/></th>
</xsl:for-each>
</xsl:if>
</tr>
<tr>
<xsl:for-each select="*">
<th>
<xsl:value-of select="."/>
</th>
</xsl:for-each>
</tr>
</table>
<p></p>
</xsl:for-each>
</body>
</html>
</xsl:template>
</xsl:stylesheet>
and this is the output:
(source: UploadScreenshot.com)
What is happening is that every time a new table is being created for each sub-element but what I want is a smart code that will make automatically all 3 tables and then make the headers and sub-headers and after that it will input the data. What I want is something like this (ignore the colors and the text formatting):
What I want my XSLT code to do is create one table for each element instance, then put the name of the element as a header, after that put as headers the sub-elements once and then fill the table with the data.
Upvotes: 1
Views: 5140
Reputation: 101730
The approach to use here is to use Muenchian grouping and keys to find distinct groups and members of them. I believe this should work:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes" omit-xml-declaration="yes"/>
<xsl:key name="kCategory" match="/*/*" use="local-name()"/>
<xsl:template match="/*">
<div>
<xsl:apply-templates
select="*[generate-id() = generate-id(key('kCategory', local-name())[1])]" />
</div>
</xsl:template>
<xsl:template match="/*/*">
<table>
<tr class="table-header odd-row">
<th colspan="3">
<xsl:value-of select="local-name()"/>
</th>
</tr>
<tr class="column-headers">
<xsl:apply-templates select="*" mode="colHeader" />
</tr>
<xsl:apply-templates select="key('kCategory', local-name())" mode="row"/>
</table>
</xsl:template>
<xsl:template match="/*/*/*" mode="colHeader">
<td>
<xsl:value-of select="local-name()"/>
</td>
</xsl:template>
<xsl:template match="/*/*" mode="row">
<tr>
<xsl:if test="position() mod 2 = 1">
<xsl:attribute name="class">odd-row</xsl:attribute>
</xsl:if>
<xsl:apply-templates select="*" mode="value" />
</tr>
</xsl:template>
<xsl:template match="/*/*/*" mode="value">
<td>
<xsl:value-of select="." />
</td>
</xsl:template>
</xsl:stylesheet>
When run on your sample input, this produces:
<div>
<table>
<tr class="table-header odd-row">
<th colspan="3">Trainee</th>
</tr>
<tr class="column-headers">
<td>Trainee_ID</td>
<td>Trainee_Name</td>
<td>Trainee_Age</td>
</tr>
<tr class="odd-row">
<td>1521</td>
<td>Mary Andersen</td>
<td>23</td>
</tr>
<tr>
<td>1522</td>
<td>Jane Sellers</td>
<td>56</td>
</tr>
<tr class="odd-row">
<td>1523</td>
<td>Julie Aniston</td>
<td>32</td>
</tr>
</table>
<table>
<tr class="table-header odd-row">
<th colspan="3">Class</th>
</tr>
<tr class="column-headers">
<td>Trainee_ID</td>
<td>Course_ID</td>
<td>Class_Room</td>
</tr>
<tr class="odd-row">
<td>1521</td>
<td>A21</td>
<td>A1</td>
</tr>
<tr>
<td>1522</td>
<td>A22</td>
<td>B2</td>
</tr>
<tr class="odd-row">
<td>1523</td>
<td>B24</td>
<td>B3</td>
</tr>
</table>
<table>
<tr class="table-header odd-row">
<th colspan="3">Course</th>
</tr>
<tr class="column-headers">
<td>Course_ID</td>
<td>Course_Title</td>
<td>Course_Hours_Per_Week</td>
</tr>
<tr class="odd-row">
<td>A21</td>
<td>Yoga</td>
<td>2</td>
</tr>
<tr>
<td>A22</td>
<td>Pilates</td>
<td>2</td>
</tr>
<tr class="odd-row">
<td>B24</td>
<td>Aerobic</td>
<td>3</td>
</tr>
</table>
</div>
I presume you can handle the colors and styling? I've added two class
attributes where I thought they'd be appropriate.
You should be able to use CSS like the following to get the styles you need (it will surely need some tweaking to get it just right, but this is the general idea:
th, td
{
border: 2px solid #9BBA58;
background-color: #E6EDD4;
}
tr.table-header th, tr.column-headers td
{
font-weight: bold;
}
tr.table-header th
{
border-bottom: 4px solid #9BBA58;
}
tr.odd-row th, tr.odd-row td
{
background-color: white;
}
Upvotes: 3