0

How do I create table with dynamic number of columns with xsl fo. The number of columns varies with each input file but fixed for a single input file.

Here is a sample xml

<root>
<ColNo>3</ColNo>
<Objects>
<object id="1">
 <prop1 old="5" new="7">
 <prop2 old="2" new="1">
 <prop3 old="3" new="6">
</object>
</Objects>
</root>

I want a table with like below

Obj1
------------------------------------------
 prop1     | prop2          | prop3 
-------------------------------------------
old | new  |  old | new     | old  | new
-------------------------------------------
5   | 7    |  2   |  1      | 3    | 6

I came across to number-columns-repeated properties.. But unable to understand how to use it..

Also let me if there is any better approach.

Thanks.

Web-E
  • 1,169
  • 2
  • 12
  • 17

1 Answers1

1

You don't indicate how much you have tried, but I suspect not much since the input data are not well formed. And you also don't indicate how multiple rows of data are represented, so I've made a guess below by modifying the data adding additional information.

A complete solution follows. It uses the two available row-filling techniques and is similar to an illustration I use in the classroom (as well as an exercise) that engages them.

When I run the output below through a conforming XSL-FO processor, I get the table outlined as you have asked.

t:\ftemp>type table.xml 
<root>
<ColNo>3</ColNo>
<Objects>
<object id="1">
 <prop1 old="5" new="7"/>
 <prop2 old="2" new="1"/>
 <prop3 old="3" new="6"/>
 <prop1 old="15" new="17"/>
 <prop2 old="12" new="11"/>
 <prop3 old="13" new="16"/>
</object>
</Objects>
</root>

t:\ftemp>call xslt table.xml table.xsl table.fo 

t:\ftemp>type table.fo 
<?xml version="1.0" encoding="utf-8"?>
<root xmlns="http://www.w3.org/1999/XSL/Format" font-family="Times" font-size="20pt">
   <layout-master-set>
      <simple-page-master master-name="frame" page-height="210mm" page-width="297mm" margin-top="1cm" margin-bottom="1cm" margin-left="1cm" margin-right="1cm">
         <region-body region-name="frame-body"/>
      </simple-page-master>
   </layout-master-set>
   <page-sequence master-reference="frame">
      <flow flow-name="frame-body">
         <block>Obj1</block>
         <table border="solid 1pt" text-align="center">
            <table-header>
               <table-row>
                  <table-cell number-columns-spanned="2" border="solid 1pt">
                     <block>prop1</block>
                  </table-cell>
                  <table-cell number-columns-spanned="2" border="solid 1pt">
                     <block>prop2</block>
                  </table-cell>
                  <table-cell number-columns-spanned="2" border="solid 1pt">
                     <block>prop3</block>
                  </table-cell>
               </table-row>
               <table-row>
                  <table-cell border="solid 1pt">
                     <block>old</block>
                  </table-cell>
                  <table-cell border="solid 1pt">
                     <block>new</block>
                  </table-cell>
                  <table-cell border="solid 1pt">
                     <block>old</block>
                  </table-cell>
                  <table-cell border="solid 1pt">
                     <block>new</block>
                  </table-cell>
                  <table-cell border="solid 1pt">
                     <block>old</block>
                  </table-cell>
                  <table-cell border="solid 1pt">
                     <block>new</block>
                  </table-cell>
               </table-row>
            </table-header>
            <table-body>
               <table-cell border="solid 1pt">
                  <block>5</block>
               </table-cell>
               <table-cell border="solid 1pt">
                  <block>7</block>
               </table-cell>
               <table-cell border="solid 1pt">
                  <block>2</block>
               </table-cell>
               <table-cell border="solid 1pt">
                  <block>1</block>
               </table-cell>
               <table-cell border="solid 1pt">
                  <block>3</block>
               </table-cell>
               <table-cell border="solid 1pt" ends-row="true">
                  <block>6</block>
               </table-cell>
               <table-cell border="solid 1pt">
                  <block>15</block>
               </table-cell>
               <table-cell border="solid 1pt">
                  <block>17</block>
               </table-cell>
               <table-cell border="solid 1pt">
                  <block>12</block>
               </table-cell>
               <table-cell border="solid 1pt">
                  <block>11</block>
               </table-cell>
               <table-cell border="solid 1pt">
                  <block>13</block>
               </table-cell>
               <table-cell border="solid 1pt" ends-row="true">
                  <block>16</block>
               </table-cell>
            </table-body>
         </table>
      </flow>
   </page-sequence>
</root>
t:\ftemp>type table.xsl 
<?xml version="1.0" encoding="US-ASCII"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                xmlns="http://www.w3.org/1999/XSL/Format" 
                version="1.0">

<xsl:output indent="yes"/>

<xsl:template match="/">
<root font-family="Times" font-size="20pt">

  <layout-master-set>
    <simple-page-master master-name="frame" 
                        page-height="210mm" page-width="297mm" 
                        margin-top="1cm" margin-bottom="1cm" 
                        margin-left="1cm" margin-right="1cm">
      <region-body region-name="frame-body"/>
    </simple-page-master>
  </layout-master-set>

  <page-sequence master-reference="frame">
    <flow flow-name="frame-body">
      <!--reposition to the top of table data-->
      <xsl:for-each select="root/Objects/object">
        <block>Obj<xsl:value-of select="@id"/></block>
        <table border="solid 1pt" text-align="center">
          <table-header>
            <!--header rows - use row-based row-grouping strategy-->
            <table-row>
              <xsl:for-each select="*[position() &lt;= /root/ColNo]">
                <table-cell number-columns-spanned="2" border="solid 1pt">
                  <block><xsl:value-of select="name(.)"/></block>
                </table-cell>
              </xsl:for-each>
            </table-row>
            <table-row>
              <xsl:for-each select="*[position() &lt;= /root/ColNo]">
                <table-cell border="solid 1pt"><block>old</block></table-cell>
                <table-cell border="solid 1pt"><block>new</block></table-cell>
              </xsl:for-each>
            </table-row>
          </table-header>
          <table-body>
            <!--body rows - use cell-based row-grouping strategy-->
            <xsl:apply-templates select="*"/>
          </table-body>
        </table>
      </xsl:for-each>
    </flow>
  </page-sequence>
</root>
</xsl:template>

<xsl:template match="object/*">
  <table-cell border="solid 1pt">
    <block><xsl:value-of select="@old"/></block>
  </table-cell>
  <table-cell border="solid 1pt">
    <xsl:if test="position() mod /root/ColNo = 0">
      <!--every time the last item of a row is encountered, signal end-->
      <xsl:attribute name="ends-row">true</xsl:attribute>
    </xsl:if>
    <block><xsl:value-of select="@new"/></block>
  </table-cell>
</xsl:template>

</xsl:stylesheet>


t:\ftemp>start table.fo 

t:\ftemp>rem Done! 
G. Ken Holman
  • 4,333
  • 16
  • 14
  • Thanks for the answer. Sorry for not being clear in question. I am very new to this, I solved the my problem in very ugly way. I will try to understand and implement this. Just a quick question for the learning purpose, what is this `` does? and what is `name(.)` function. – Web-E Aug 22 '13 at 04:24
  • The XPath address `*[position() <= /root/ColNo]` first addresses all child elements, and then only keeps those whose position in the set of child elements selected is less than or equal to the column number. So, in your data's example, positions 1, 2 and 3. The `name()` function is an exposition function that exposes the name of the node addressed (or the current node if there are no address arguments). Your output data has, for example, `prop1` and the only place I saw `prop1` was the name of the element. – G. Ken Holman Aug 22 '13 at 14:21