Search code examples
xmlsortingxsltxsl-foapache-fop

Sorting data in xsl fo for PDF output using XSLT V 1.0


This is a question that I realize has been asked a million different ways already with just as many different solutions, but try as I might, I just can't find a solution that will give me the desired effect I want. I am creating xsl fo templates to give to Apache FOP to render a PDF. The PDF I am rendering is fairly straight forward, it simply has a table with repeating rows in it, and I want to control the order that the rows render in based on sorting a child tag of the repeating node I selected based on it's integer value.

My XML looks like this...

  <?xml version="1.0" encoding="UTF-8"?>
    <Stops>
     <Stop>
      <Sequence>1</Sequence>
       <Address>
       <Street>123 main st</Street>
       <City>Toronto</City>
       <Postal_Code>L1G4F4</Postal_Code>
      </Address>
     </Stop>
     <Stop>
      <Sequence>3</Sequence>
      <Address>
       <Street>1441 First st</Street>
       <City>Toronto</City>
       <Postal_Code>L1G4F4</Postal_Code>
      </Address>
     </Stop>
     <Stop>
      <Sequence>2</Sequence>
      <Address>
       <Street>467 Center st</Street>
       <City>Vancouver</City>
       <Postal_Code>L9V4A1</Postal_Code>
      </Address>
     </Stop>
    </Stops>

My xslfo code looks like...

<fo:table> 
 <fo:table-column column-width="proportional-column-width(50)" column-number="1"/>
 <fo:table-column column-width="proportional-column-width(50)" column-number="2"/>
 <fo:table-body>
  <xsl:call-template name="sort-stops"/>
  </fo:table-body>
</fo:table>

With...

<xslt:template select="Stops" name="sort-stops">
 <xsl:sort slect="stop/sequence"/>
 <xsl:for-each select="stop">
  <fo:table-row>
   <fo:table-cell>
    <fo:block>
     <xsl:value-of select="sequence"/>
    </fo:block>
   </fo:table-cell>
  </fo:table-row>
 </xsl:for-each>
</xslt:template>

There is obviously something wrong with my approach to this, but I have no idea where to go from here anymore. If I stick in simple code to insert a block in my sort-stops template definition, that will show up, but it really seems like the problem is in how I access the child nodes of my stop element. Can anyone please give me some direction as to how I can have the table rows sort the stops based on the sequence tag and genreate repeating rows in this order?


Solution

  • You didn't post your required output, but I am guessing you want to do something like this:

    XSLT 1.0

    <xsl:stylesheet version="1.0" 
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:fo="http://www.w3.org/1999/XSL/Format">
    <xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
    
    <xsl:template match="/">
        <fo:table> 
            <fo:table-column column-width="proportional-column-width(50)" column-number="1"/>
            <fo:table-column column-width="proportional-column-width(50)" column-number="2"/>
            <fo:table-body>
                <xsl:apply-templates select="Stops/Stop">
                    <xsl:sort select="Sequence" data-type="number" order="ascending"/>
                </xsl:apply-templates>
            </fo:table-body>
        </fo:table>
    </xsl:template>
    
    <xsl:template match="Stop">
        <fo:table-row>
            <fo:table-cell>
                <fo:block>
                    <xsl:value-of select="Sequence"/>
                </fo:block>
            </fo:table-cell>
        </fo:table-row>
    </xsl:template>
    
    </xsl:stylesheet>
    

    which given your example input will produce:

    <?xml version="1.0" encoding="UTF-8"?>
    <fo:table xmlns:fo="http://www.w3.org/1999/XSL/Format">
       <fo:table-column column-width="proportional-column-width(50)" column-number="1"/>
       <fo:table-column column-width="proportional-column-width(50)" column-number="2"/>
       <fo:table-body>
          <fo:table-row>
             <fo:table-cell>
                <fo:block>1</fo:block>
             </fo:table-cell>
          </fo:table-row>
          <fo:table-row>
             <fo:table-cell>
                <fo:block>2</fo:block>
             </fo:table-cell>
          </fo:table-row>
          <fo:table-row>
             <fo:table-cell>
                <fo:block>3</fo:block>
             </fo:table-cell>
          </fo:table-row>
       </fo:table-body>
    </fo:table>
    

    Note that XML is case-sensitive; select="sequence" does not select <Sequence>.