1

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?

1
  • 1
    Perhaps this will help you get started: stackoverflow.com/questions/24252603/… It's practically the same problem. You can use the same stylesheet, adapt it to XSL-FO, and change the number of columns to 5. Commented Jun 19, 2014 at 16:21

2 Answers 2

6

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>
Sign up to request clarification or add additional context in comments.

Comments

0

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 &gt; 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 &gt; 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>

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.