Reputation: 733
I am having an issue while trying to aggregate xml duplicates while trying to produce a formatted pdf with XSL-FO, XSLT and Apache FOP.
I have already read a few posts about how to implement this with special look to post: XSLT and xpath v1.0 find duplicates and aggregate and also to the article Muenchian Grouping but still I face a problem.
My XML is the following:
<Document>
<RecordDetails>
<Record>
<contact id="0001" title="Mr" forename="John" surname="Smith" />
</Record>
<Record>
<contact id="0002" title="Dr" forename= "Amy" surname="Jones" />
</Record>
<Record>
<contact id="0003" title="Mr" forename="Jack" surname="Smith" />
</Record>
</RecordDetails>
</Document>
What I am trying to implement is aggregate on my pdf formatted output all the contact items that that have the same value for the srname attribute. My current XSL code looks like that:
<xsl:key name="contactsbysurname" match="contact" use="@surname"/>
<xsl:template match="Record">
*<xsl:for-each select="contact[generate-id(.)=generate-id(key('contactsbysurname',@surname)[1])]">
<xsl:for-each select="key('contactsbysurname',@surname)"> *
<fo:table-row font-size="6.5pt">
<xsl:apply-templates select="contact"/>
</fo:table-row>
</xsl:for-each>
</xsl:for-each>
</xsl:template>
<xsl:template match="contact">
<fo:table-cell>
<fo:block text-align="center">
<xsl:value-of select="@title" />
</fo:block>
</fo:table-cell>
<fo:table-cell>
<fo:block text-align="center">
<xsl:value-of select="@Smith" />
</fo:block>
</fo:table-cell>
<fo:table-cell>
<fo:block text-align="center">
...
</fo:block>
</fo:table-cell>
</xsl:template>
It seems that within the template Record when I try to include the 2 xsl:for-each loops in order to implement the Muenchian method, FOP returns me an error which involves that no fo:table-cell elements can be found inside the fo:table-row tag. I understand this as the fact that FOP cannot access the first xsl:for-each loop and I wonder if I am doing something wrong.
If I remove the 2 for-each tags I get my pdf but without the aggregation. Am I missing something on the way I write my code ?
All help is very appreciated! Thank you.
Upvotes: 1
Views: 288
Reputation: 101748
It should work as you're expecting if you simply change this line:
<xsl:apply-templates select="contact"/>
to this:
<xsl:apply-templates select="."/>
However, I would propose structuring your XSLT more like this:
<xsl:template match="Record">
<xsl:apply-templates
select="contact[generate-id() =
generate-id(key('contactsbysurname',@surname)[1])]"
mode="group" />
</xsl:template>
<xsl:template match="contact" mode="group">
<xsl:apply-templates select="key('contactsbysurname',@surname)" />
</xsl:template>
<xsl:template match="contact">
<fo:table-row font-size="6.5pt">
<fo:table-cell>
<fo:block text-align="center">
<xsl:value-of select="@title" />
</fo:block>
</fo:table-cell>
<fo:table-cell>
<fo:block text-align="center">
<xsl:value-of select="@surname" />
</fo:block>
</fo:table-cell>
<fo:table-cell>
<fo:block text-align="center">
...
</fo:block>
</fo:table-cell>
</fo:table-row>
</xsl:template>
Edit
If you just want to show one row of information for each contact, then there's no need to have two nestes for-each
es or two layers of contact
templates. In that case, it's even simpler:
<xsl:template match="Record">
<xsl:apply-templates
select="contact[generate-id() =
generate-id(key('contactsbysurname',@surname)[1])]" />
</xsl:template>
<xsl:template match="contact">
<fo:table-row font-size="6.5pt">
<fo:table-cell>
<fo:block text-align="center">
<xsl:value-of select="@title" />
</fo:block>
</fo:table-cell>
<fo:table-cell>
<fo:block text-align="center">
<xsl:value-of select="@surname" />
</fo:block>
</fo:table-cell>
<fo:table-cell>
<fo:block text-align="center">
...
</fo:block>
</fo:table-cell>
</fo:table-row>
</xsl:template>
Upvotes: 1
Reputation: 167716
Change
<xsl:template match="Record">
*<xsl:for-each select="contact[generate-id(.)=generate-id(key('contactsbysurname',@surname)[1])]">
<xsl:for-each select="key('contactsbysurname',@surname)"> *
<fo:table-row font-size="6.5pt">
<xsl:apply-templates select="contact"/>
</fo:table-row>
</xsl:for-each>
</xsl:for-each>
</xsl:template>
to
<xsl:template match="RecordDetails">
<xsl:for-each select="Record/contact[generate-id(.)=generate-id(key('contactsbysurname',@surname)[1])]">
<fo:table-row font-size="6.5pt">
<xsl:apply-templates select="key('contactsbysurname',@surname)"/>
</fo:table-row>
</xsl:for-each>
</xsl:for-each>
</xsl:template>
That should give you one row for each group of contact
with the same @surname
. Then in each row you process all contact
elements with the matching template.
And
<xsl:value-of select="@Smith" />
looks wrong as well as there are no attributes of that name in the input sample.
Upvotes: 0