This articles describes how to create grouping logic in XSL-FO using Apache
FOP. A common requirement while creating reports is to group data by a given
field.
While XSLT 2.0 provides a way to do this, XSLT 1.0 does not provide any functionality in this regard.
If you use Apache FOP for PDF generation you might need to group your XML data using XSLT 1.0 since this is what Apache Xalan supports today.
This document describes a simple example for grouping data. The input file will be a "flat" XML file
like the following one:
<?xml version="1.0"?>
<cars>
<car>
<licenseid>1254-TH</licenseid>
<color>red</color>
</car>
<car>
<licenseid>2254-FH</licenseid>
<color>blue</color>
</car>
<car>
<licenseid>2264-FT</licenseid>
<color>blue</color>
</car>
<car>
<licenseid>4464-FT</licenseid>
<color>green</color>
</car>
<car>
<licenseid>2211-GT</licenseid>
<color>red</color>
</car>
</cars>
this file which is a flat list of cars will be converted to a PDF file that shows the list of cars grouped by color. The result will look like this:
this can be done with the XSL-FO file listed at the bottom of this page, which is now explained, it involves 3 steps:
- we create a key that assigns each
car a key value that is the color of the car
<xsl:key name="bykey1317757767468" match="cars/car" use="color" />
once this key has been defined we can access all cars of a give color, for example:
key('bykey1317757767468', 'red') would
return all cars that have red color.
- now we need to get the list of different car's colors in the list, this is done with
<xsl:for-each select="cars/car[count(. | key('bykey1317757767468', color)[1]) = 1]">
what is expression is doing is, check every car and select it only if it is the first one in the list returned by
key('bykey1317757767468', color), that is the first car in the list of cars that have the same color.
In this way each color is selected only once.
- and finally for each color we need to get the cars that have such color
<xsl:for-each select="key('bykey1317757767468', color)">
If you want to test this yourself you will need to download Apache
FOP. After that you copy the XML data to a file called cars.xml and the xsl
file to groupcars.xsl. Once you have these 2 files you run Apache FOP like this:
fop.bat -xml cars.xml -xsl groupcars.xsl -pdf groups.pdf
Do you want to know more?
If you want to learn more about this technique there is a good article
here.
If on the other hand you do not want to get involved with these details, our J4L
FO Designer will create the XSL-FO file for you.
XSL-FO file for grouping:
<?xml version="1.0" encoding="UTF-8"?><xsl:transform version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" >
<xsl:key name="bykey1317757767468" match="cars/car" use="color" />
<xsl:template match="/"><fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format">
<fo:layout-master-set>
<fo:simple-page-master master-name="master0" page-width="21.0cm" page-height="29.7cm" margin-top="1.5cm" margin-left="1.5cm" >
<fo:region-body region-name="body0" />
</fo:simple-page-master>
</fo:layout-master-set>
<fo:page-sequence master-reference="master0">
<fo:flow flow-name="body0">
<!-- group start, get list of different colors-->
<xsl:for-each select="cars/car[count(. | key('bykey1317757767468', color)[1]) = 1]">
<fo:block font-size="14pt" font-family="SansSerif" color="#0000FF" text-align="left">
Group for color <xsl:value-of select="color"></xsl:value-of>
</fo:block>
<!-- for all cars in current color-->
<xsl:for-each select="key('bykey1317757767468', color)">
<fo:block font-size="10pt" font-family="SansSerif" color="#000000" text-align="left"> <xsl:value-of
select="licenseid"></xsl:value-of></fo:block>
</xsl:for-each>
</xsl:for-each>
</fo:flow>
</fo:page-sequence>
</fo:root>
</xsl:template>
</xsl:transform>
|