tags:

views:

225

answers:

4

I have the following XML:

<?xml version="1.0" encoding="utf-8" ?>
<data>
 <record>
  <id>1</id>
  <name>David</name>
  <age>40</age> 
 </record>
 <record>
  <id>2</id>
  <name>Tully</name>
  <age>38</age>
 </record>
 <record>
  <id>3</id> 
  <name>Solai</name>
  <age>32</age>
 </record>
 <record>
  <id>4</id> 
  <name>Michael</name>
  <age>49</age>
 </record>
 <record>
  <id>5</id> 
  <name>Tony</name>
  <age>19</age>
 </record>
 <record>
  <id>6</id> 
  <name>Ray</name>
  <age>26</age>
 </record>
 <record>
  <id>7</id> 
  <name>Leeha</name>
  <age>13</age>
 </record>

</data>

I want to display the records as similar to a dataview in asp.net, like the following:

record 1   record2   record3   record4
record 5   record6   record7   record8

and so on.

I have the following XSL at the moment which is shakey to say the least!

<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"&gt;

<xsl:template match="/">

 <div>
 <table style="width: 200px" border="1">

  <tr>
  <xsl:for-each select="data/record">

   <xsl:if test="position() mod 4 = 0">
   <tr></tr>
   </xsl:if> 

   <td>
    <xsl:value-of select="name"></xsl:value-of>
    <br />
    <xsl:value-of select="age"></xsl:value-of>
   </td>

  </xsl:for-each>
  </tr>

 </table>
 </div>

</xsl:template>

</xsl:stylesheet>

So my question is, am I on the correct path here.. or is there a more simple, robust way to achieve this?

Many thanks in advance.

A: 

I think you are roughly on the right lines, but you have an empty tr embedded in another tr which isn't going to do anything useful and may foul up browsers. If you post the HTML you expect then it will be clearer how to achieve it.

peter.murray.rust
+1  A: 

Check this out: How can I break a table row in xsl after a specified count?

Or also this:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"&gt;

  <xsl:template match="/">

    <div>
      <table style="width: 200px" border="1">

        <tr>
          <xsl:for-each select="data/record">
            <td>
              <xsl:value-of select="name" /><br />
              <xsl:value-of select="age" />
            </td>
            <xsl:if test="position() mod 4 = 0 and position() != last()">
              <xsl:text disable-output-escaping="yes">
                 &lt;/tr&gt;&lt;tr&gt;
              </xsl:text>
            </xsl:if>
          </xsl:for-each>
        </tr>

      </table>
    </div>

  </xsl:template>

</xsl:stylesheet>
Rubens Farias
The second answer is simple and effective - there is a "gotcha" though, you need to catch for the case where the last row has exactly four items and *not* drop the extra break in.
Murph
@Murph: I can't test it right now but a `and position != last()` should do the trick; I edited my answer to reflect this, ty
Rubens Farias
@Rubens: I agree (I resisted the temptation to exercise my shiny new "edit" rights to make exactly the same change - not quite worked out the ettiquette of that yet!)
Murph
Hi guys! thanks so much for all the examples.. really very useful. I like the simplicity of this answer, although I can see why the other answers (from Erlock below and the one above) have more flexibility perhaps.. but either way, really many thanks!
David S
+1  A: 
  <xsl:template match="/data">
    <div>
       <table style="width: 200px" border="1">
        <xsl:apply-templates select="record[position() mod 4 = 1]"/>
      </table>
    </div>
  </xsl:template>
  <xsl:template match="record">
    <row>
      <xsl:apply-templates select=". | following-sibling::record[position() &lt; 4]" mode="mode" />
    </row>
  </xsl:template>
  <xsl:template match="record" mode="mode">
    <cell>
      <xsl:value-of select="id"/>
    </cell>
    <xsl:if test="position() = last() and position() &lt; 3">
      <xsl:call-template name="complete-row">
        <xsl:with-param name="count" select="position()"/>
      </xsl:call-template>
    </xsl:if>
  </xsl:template>
  <xsl:template name="complete-row">
     <xsl:param name="count"/>
     <td>&#160;</td>
     <xsl:if test="($count + 1) &lt; 3">
        <xsl:call-template name="complete-row">
           <xsl:with-param name="count" select="$count + 1"/>
        </xsl:call-template>
     </xsl:if>
  </xsl:template>
mikesub
forgot to change 'mode' name from default to something more sensible
mikesub
@Rubens Farias: hacking node-tree output with xsl:text disable-output-escaping is no good.
mikesub
I agree, mikesub
Rubens Farias
A: 

I wrote this a while ago (I've just adapted it to suit your XML schema):

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"&gt; 

   <xsl:output method="html"/> 

   <xsl:variable name="M" select="5"/> 
   <xsl:variable name="C" select="2"/> 

   <xsl:template match="/"> 
      <html> 
        <body> 
           <table border="1"> 
             <xsl:for-each select="/data/record[position() mod ($M * $C) = 1]"> 
                <tr>
                  <xsl:for-each select=".|following-sibling::record[position() mod $M = 0 and position() &lt; $M * $C]">
                   <td>
                     <xsl:for-each select=".|following-sibling::record[position() &lt; $M]">
                      <xsl:value-of select="name"/><br/>
                      <xsl:value-of select="age"/>
                     </xsl:for-each>
                   </td>
                  </xsl:for-each>
                  <xsl:if test="position() = last()">
                   <xsl:call-template name="empty-cells">
                     <xsl:with-param name="nb" select="$C - ceiling(count(.|following-sibling::record) div $M)"/>
                   </xsl:call-template>
                  </xsl:if>
       </tr>
             </xsl:for-each> 
           </table> 
        </body> 
      </html> 
   </xsl:template> 

   <xsl:template name="empty-cells">
    <xsl:param name="nb"/>
    <xsl:if test="$nb &gt;= 1">
     <td/>
       <xsl:call-template name="empty-cells">
         <xsl:with-param name="nb" select="$nb - 1"/>
       </xsl:call-template>
    </xsl:if>    
   </xsl:template>


</xsl:stylesheet>

Variable C is the number of columns, and variable M is the number of records per cell.

Erlock