I'm fairly new to XSLT and I am stuck with a problem where I have an Element with an unknown amount of children, and I need to display these children in a table such that there are 5-6 columns available to display the information.
If I'm given an XML file that looks like this:
<books>
<book>
<author>Ralls, Kim</author>
<title>Midnight Rain</title>
</book>
<book>
<author>Corets, Eva</author>
<title>Maeve Ascendant</title>
</book>
<book>
<author>Corets, Eva</author>
<title>Oberon's Legacy</title>
</book>
<book>
<author>Randall, Cynthia</author>
<title>Lover Birds</title>
</book>
<book>
<author>Thurman, Paula</author>
<title>Splish Splash</title>
</book>
<book>
<author>Knorr, Stefan</author>
<title>Creepy Crawlies</title>
</book>
<book>
<author>Kress, Peter</author>
<title>Paradox Lost</title>
</book>
<book>
<author>Crichton, Michael</author>
<title>Jurassic Park</title>
</book>
<book>
<author>Orwell, George</author>
<title>1984</title>
</book>
<book>
<author>Martin, George</author>
<title>A Song of Ice And Fire</title>
</book>
</books>
I would like to display these 10 books in a table consisting of two rows and five columns.
I've gotten this far:
<xsl:template match="books" mode="table">
<fo:table margin-left="auto" margin-right="auto">
<fo:table-body>
<fo:table-row table-layout="fixed">
<xsl:for-each select="skill">
<fo:table-cell border="1">
<fo:block font-weight="bold">
<xsl:value-of select="name"/>
</fo:block>
</fo:table-cell>
</xsl:for-each>
</fo:table-row>
</fo:table-body>
</fo:table>
</xsl:template>
But all this will do is put every cell on the same row. I'm looking for a way to check if the loop has run a certain amount of times (5 or 6), and inserting a new row when that happens, but I don't know if that's something I can do in XSL.
Can anyone point me in the right direction?
The previous answers are way to complicated for what should be (and is) easy. Recursion is not required for such a simple thing.
In XSL FO you do not need to structure tables with rows. You can use the attribute "ends-row" to specify that you are ending a row and starting a new one. You can easily adapt this simple example and even pass in the "number of columns" (see the mod 5 ... which means after every fifth one start a new row ... change to 4 or 8 or whatever you wish) ... You would just create the structure for the table (fo:table and fo:table-body) outside of this. Inside the table body just put cells as children like this template does:
<xsl:template match="book">
<xsl:variable name="pos" select="position()"/>
<fo:table-cell>
<xsl:if test="not($pos mod 5)">
<xsl:attribute name="ends-row">true</xsl:attribute>
</xsl:if>
<fo:block>
<xsl:value-of select="author"/>
</fo:block>
</fo:table-cell>
</xsl:template>
So putting this into a simple example with your data ... see below. Formats your XML into five columns per row.
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:fo="http://www.w3.org/1999/XSL/Format"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
version="1.0">
<xsl:template match="/">
<fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format">
<fo:layout-master-set>
<fo:simple-page-master margin-top="1in" margin-left="1in" margin-bottom="1in"
margin-right="1in" page-width="8in" page-height="11in" master-name="first">
<fo:region-body margin-top="0pt"/>
<fo:region-before extent="0pt"/>
<fo:region-after extent="0pt"/>
</fo:simple-page-master>
</fo:layout-master-set>
<fo:page-sequence master-reference="first">
<fo:flow flow-name="xsl-region-body" font-size="12pt" font-family="Helvetica">
<xsl:apply-templates/>
</fo:flow>
</fo:page-sequence>
</fo:root>
</xsl:template>
<xsl:template match="books">
<fo:table>
<fo:table-body>
<xsl:apply-templates/>
</fo:table-body>
</fo:table>
</xsl:template>
<xsl:template match="book">
<xsl:variable name="pos" select="position()"/>
<fo:table-cell border="1pt solid black">
<xsl:if test="not($pos mod 5)">
<xsl:attribute name="ends-row">true</xsl:attribute>
</xsl:if>
<fo:block>
<xsl:value-of select="author"/>
</fo:block>
</fo:table-cell>
</xsl:template>
</xsl:stylesheet>
One approach is to use two recursive templates:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:ext="http://exslt.org/common"
xmlns:fo="http://www.w3.org/1999/XSL/Format"
exclude-result-prefixes="ext">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<!-- set the number of columns you want globally -->
<xsl:param name="set-cols" select="'5'"/>
<xsl:template match="books">
<!-- count the needed rows -->
<xsl:variable name="set-row" select="ceiling(count(book) div $set-cols)"/>
<fo:table margin-left="auto" margin-right="auto">
<fo:table-body>
<xsl:call-template name="rows">
<xsl:with-param name="books">
<xsl:apply-templates/>
</xsl:with-param>
<xsl:with-param name="set-row" select="$set-row"/>
</xsl:call-template>
</fo:table-body>
</fo:table>
</xsl:template>
<!-- rows -->
<xsl:template name="rows">
<xsl:param name="books" select="''"/>
<xsl:param name="set-row" select="''"/>
<xsl:param name="count-rows" select="'0'"/>
<xsl:if test="$set-row > 0">
<fo:table-row table-layout="fixed">
<xsl:call-template name="cols">
<xsl:with-param name="books" select="$books"/>
<xsl:with-param name="count-rows" select="$count-rows"/>
</xsl:call-template>
</fo:table-row>
<xsl:call-template name="rows">
<xsl:with-param name="books" select="$books"/>
<xsl:with-param name="set-row" select="$set-row - 1"/>
<xsl:with-param name="count-rows" select="$count-rows + 1"/>
</xsl:call-template>
</xsl:if>
</xsl:template>
<!-- columns -->
<xsl:template name="cols">
<xsl:param name="books" select="''"/>
<xsl:param name="cols" select="$set-cols"/>
<xsl:param name="count-rows" select="''"/>
<xsl:param name="count-cols" select="'1'"/>
<xsl:if test="$cols > 0">
<fo:table-cell border="1">
<fo:block font-weight="bold">
<xsl:variable name="book" select="ext:node-set($books)//book[position() = ($count-rows * $set-cols + $count-cols)]"/>
<xsl:value-of select="$book/author"/>
</fo:block>
</fo:table-cell>
<xsl:call-template name="cols">
<xsl:with-param name="books" select="$books"/>
<xsl:with-param name="cols" select="$cols - 1"/>
<xsl:with-param name="count-rows" select="$count-rows"/>
<xsl:with-param name="count-cols" select="$count-cols + 1"/>
</xsl:call-template>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
With yout input
<books>
<book>
<author>Ralls, Kim</author>
<title>Midnight Rain</title>
</book>
<book>
<author>Corets, Eva</author>
<title>Maeve Ascendant</title>
</book>
<!-- ... -->
</books>
you get:
<fo:table margin-left="auto" margin-right="auto" xmlns:fo="http://www.w3.org/1999/XSL/Format">
<fo:table-body>
<fo:table-row table-layout="fixed">
<fo:table-cell border="1">
<fo:block font-weight="bold">Ralls, Kim</fo:block>
</fo:table-cell>
<fo:table-cell border="1">
<fo:block font-weight="bold">Corets, Eva</fo:block>
</fo:table-cell>
<fo:table-cell border="1">
<fo:block font-weight="bold">Corets, Eva</fo:block>
</fo:table-cell>
<fo:table-cell border="1">
<fo:block font-weight="bold">Randall, Cynthia</fo:block>
</fo:table-cell>
<fo:table-cell border="1">
<fo:block font-weight="bold">Thurman, Paula</fo:block>
</fo:table-cell>
</fo:table-row>
<fo:table-row table-layout="fixed">
<fo:table-cell border="1">
<fo:block font-weight="bold">Knorr, Stefan</fo:block>
</fo:table-cell>
<fo:table-cell border="1">
<fo:block font-weight="bold">Kress, Peter</fo:block>
</fo:table-cell>
<fo:table-cell border="1">
<fo:block font-weight="bold">Crichton, Michael</fo:block>
</fo:table-cell>
<fo:table-cell border="1">
<fo:block font-weight="bold">Orwell, George</fo:block>
</fo:table-cell>
<fo:table-cell border="1">
<fo:block font-weight="bold">Martin, George</fo:block>
</fo:table-cell>
</fo:table-row>
</fo:table-body>
</fo:table>