Reputation: 119
I am creating an application that creates an XML as follows:
<?xml version="1.0" encoding="ISO-8859-1"?>
<?xml-stylesheet type="text/xsl" href="MyCDCatalog.xsl"?>
<catalog>
<cd>
<title>Empire Burlesque</title>
<artist>Bob Dylan</artist>
<price>10.90</price>
<year>1985</year>
</cd>
<cd>
<title>Hide your heart</title>
<artist>Bonnie Tyler</artist>
<price>9.90</price>
<year>1988</year>
</cd>
<cd>
<title>Greatest Hits</title>
<artist>Dolly Parton</artist>
<price>9.90</price>
<year>1982</year>
</cd>
<cd>
<title>One night only</title>
<artist>Bee Gees</artist>
<price>10.90</price>
<year>1998</year>
</cd>
<cd>
<title>Sylvias Mother</title>
<artist>Dr.Hook</artist>
<price>8.10</price>
<year>1973</year>
</cd>
<cd>
<title>Maggie May</title>
<artist>Rod Stewart</artist>
<price>8.50</price>
<year>1990</year>
</cd>
</catalog>
I also a XSL file called MyCDCatalog.xsl (below) that transforms the above XML file.
<?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="/">
<html>
<body>
<h2>My CD Collection</h2>
<table border="1">
<xsl:for-each select="catalog/cd">
<tr>
<td><xsl:value-of select="artist" />'s <xsl:value-of select="title" /> (<xsl:value-of select="year" />): $<xsl:value-of select="price" /></td>
</tr>
</xsl:for-each>
</table>
</body>
</html>
</xsl:template>
</xsl:stylesheet>
Currently, the XSL file returns all the entries in a single column. How do I get my output in 3 or more columns?
I want the output as follows:
[cd1] [cd2] [cd3]
[cd4] [cd5] [cd6]....
where
cd1 = Bob Dylan's Empire Burlesque (1985): $10.90
cd2 = Bonnie Tyler's Hide your heart (1988): $9.90
etc.
Thanks for the help.
Upvotes: 2
Views: 4444
Reputation: 70648
Another way to do this is to match the cd in the first, fourth, seventh (etc) position.
<xsl:apply-templates select="cd[(position() - 1) mod $columns = 0]" mode="first"/>
Where $columns is a parameter containing the number of columns
You can then match the cd elements in the row by looking at the relevant number of following siblings:
<xsl:apply-templates select=".|following-sibling::cd[position() < $columns]"/>
Here is the full XSLT
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="html" indent="yes" omit-xml-declaration="yes" />
<xsl:param name="columns" select="3"/>
<xsl:template match="/catalog">
<html>
<body>
<h2>My CD Collection</h2>
<table>
<xsl:apply-templates select="cd[(position() - 1) mod $columns = 0]" mode="first"/>
</table>
</body>
</html>
</xsl:template>
<xsl:template match="cd" mode="first">
<tr>
<xsl:apply-templates select=".|following-sibling::cd[position() < $columns]"/>
<xsl:if test="count(following-sibling::cd) < ($columns - 1)">
<xsl:call-template name="emptycell">
<xsl:with-param name="cells" select="$columns - 1 - count(following-sibling::cd)"/>
</xsl:call-template>
</xsl:if>
</tr>
</xsl:template>
<xsl:template match="cd">
<td>
<xsl:value-of select="concat(title, ' ', artist)"/>
</td>
</xsl:template>
<xsl:template name="emptycell">
<xsl:param name="cells"/>
<td/>
<xsl:if test="$cells > 1">
<xsl:call-template name="emptycell">
<xsl:with-param name="cells" select="$cells - 1"/>
</xsl:call-template>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
When applied to your sample XML, the following is output:
<html>
<body>
<h2>My CD Collection</h2>
<table>
<tr>
<td>Empire Burlesque Bob Dylan</td>
<td>Hide your heart Bonnie Tyler</td>
<td>Greatest Hits Dolly Parton</td>
</tr>
<tr>
<td>One night only Bee Gees</td>
<td>Sylvias Mother Dr.Hook</td>
<td>Maggie May Rod Stewart</td>
</tr>
</table>
</body>
</html>
(I'm only outputing name and artist here, but I am sure you can see how to change it to show more information)
Note that there is bit of nasty recursive template to output empty cells should the number of remaining cd elements be less than the number of columns in the row.
Change the paramater to 4, for example, then the following is output:
<table>
<tr>
<td>Empire Burlesque Bob Dylan</td>
<td>Hide your heart Bonnie Tyler</td>
<td>Greatest Hits Dolly Parton</td>
<td>One night only Bee Gees</td>
</tr>
<tr>
<td>Sylvias Mother Dr.Hook</td>
<td>Maggie May Rod Stewart</td>
<td />
<td />
</tr>
</table>
Upvotes: 3
Reputation: 3696
In XSLT 1.0, use the following:
<?xml version="1.0" encoding="iso-8859-1"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="html" indent="yes"/>
<xsl:key name="grp" use="floor(count(preceding-sibling::cd) div 3)" match="catalog/cd"/>
<xsl:template match="/">
<html>
<body>
<h2>My CD Collection</h2>
<table border="1">
<xsl:apply-templates
select="catalog/cd[generate-id() =
generate-id(key('grp',floor((position() - 1) div 3))[1])]"
mode="group"/>
<!-- note: index [1] is superfluous, because generate-id applied on node-set returns the id of the first element in the set -->
</table>
</body>
</html>
</xsl:template>
<xsl:template match="catalog/cd" mode="group">
<xsl:variable
name="groupitems"
select="key('grp', floor(count(preceding-sibling::cd) div 3))"/>
<tr>
<xsl:for-each select="$groupitems">
<td>
<xsl:value-of select="artist" />'s <xsl:value-of select="title" /> (<xsl:value-of select="year" />): $<xsl:value-of select="price" />
</td>
</xsl:for-each>
</tr>
</xsl:template>
</xsl:stylesheet>
Explanation: Muenchian grouping. Constructing the key gave me a little headache, because at first I tried to assign the use attribute a value related to position() - but that does not work properly. I found another thread on this site explaining what to use instead: key and position() problem.
The output is the desired one:
<html>
<body>
<h2>My CD Collection</h2>
<table border="1">
<tr>
<td>Bob Dylan's Empire Burlesque (1985): $10.90</td>
<td>Bonnie Tyler's Hide your heart (1988): $9.90</td>
<td>Dolly Parton's Greatest Hits (1982): $9.90</td>
</tr>
<tr>
<td>Bee Gees's One night only (1998): $10.90</td>
<td>Dr.Hook's Sylvias Mother (1973): $8.10</td>
<td>Rod Stewart's Maggie May (1990): $8.50</td>
</tr>
</table>
</body>
</html>
Upvotes: 2
Reputation: 12154
Edit3:
The two templates table_format
and table_format2
will do the job ..
set the count in the global variable:
<xsl:variable name="col_count" select="'3'"/>
col_count decides number of columns :)
also remember that I have used
<xsl:for-each select="catalog/cd[1]">
in the first template ..
<?xml version="1.0" encoding="iso-8859-1"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:variable name="col_count" select="'3'"/>
<xsl:template match="/">
<html>
<body>
<h2>My CD Collection</h2>
<table border="1">
<xsl:for-each select="catalog/cd[1]">
<xsl:call-template name="table_format"/>
</xsl:for-each>
</table>
</body>
</html>
</xsl:template>
<xsl:template name="table_format">
<xsl:param name="count" select="'1'"/>
<tr>
<xsl:call-template name="table_format2"/>
</tr>
<xsl:for-each select="../cd[$col_count + $count]">
<xsl:call-template name="table_format">
<xsl:with-param name="count" select="$count + $col_count"/>
</xsl:call-template>
</xsl:for-each>
</xsl:template>
<xsl:template name="table_format2">
<xsl:param name="col_count2" select="1"/>
<xsl:if test="$col_count2 <= $col_count">
<td>
<xsl:value-of select="artist" />'s <xsl:value-of select="title" /> (<xsl:value-of select="year" />): $<xsl:value-of select="price" />
</td>
<xsl:for-each select="following-sibling::cd[1]">
<xsl:call-template name="table_format2">
<xsl:with-param name="col_count2" select="$col_count2 + 1"/>
</xsl:call-template>
</xsl:for-each>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
Upvotes: 0