Pergunta

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.

Foi útil?

Solução

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>

Outras dicas

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.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top