user1130511
user1130511

Reputation: 253

XSLT Dynamic looping via Column Variables

I would like to dynamically loop through the below XML to create the following table structure:

Date | Time | Year
01/04 | 11:25:50 | 2012
01/01 | 1:05:20 | 2012

The XML structure breaks up the Columns and Rows so I need to pass the Column Description attributes and to use those to separate the Row attributes.

XML:

<Rowsets>
 <Rowset>
    <Columns>
        <Column Description="Date"/>
        <Column Description="Time"/>
        <Column Description="Year"/>
    </Columns>
    <Row>
        <Date>01/04</Date>
        <Time>11:25:50</Time>
        <Year>2012</Year>
    </Row>
    <Row>
        <Date>01/01</Date>
        <Time>1:05:20</Time>
        <Year>2012</Year>
    </Row>
 </Rowset>
</Rowsets>

My attemptted XSLT which creates the headers, but lumps the Row data into one single column:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
 <html>  
  <body>
    <table border="1" cellspacing="0">
      <!-- Generate the row of table header cells -->
      <tr>
    <xsl:variable name="currentvar" select="//@Description[1]"/>
        <xsl:for-each select="$currentvar">
          <th>
            <xsl:value-of select="."/>
          </th>
        </xsl:for-each>
      </tr>
  <xsl:for-each select="Rowsets/Rowset/Row">
   <tr>
    <td>
              <xsl:value-of select="."/>
    </td>
       </tr>
      </xsl:for-each>       
  </table> 
  </body>
 </html>
</xsl:template>
</xsl:stylesheet>

Any help is appreciated, Thanks!

Upvotes: 1

Views: 1612

Answers (2)

Wayne
Wayne

Reputation: 60424

Your stylesheet can be greatly simplified by breaking out each section of the table into its own template. This stylesheet:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:strip-space elements="*"/>
    <xsl:template match="/">
        <html>
            <body>
                <table border="1" cellspacing="0">
                    <xsl:apply-templates/>
                </table>
            </body>
        </html>
    </xsl:template>
    <xsl:template match="Columns|Row">
        <tr><xsl:apply-templates/></tr>
    </xsl:template>
    <xsl:template match="Columns/*">
        <th><xsl:apply-templates select="@Description"/></th>
    </xsl:template>
    <xsl:template match="Row/*">
        <td><xsl:apply-templates/></td>
    </xsl:template>
</xsl:stylesheet>

Produces the desired result:

<html>
   <body>
      <table border="1" cellspacing="0">
         <tr>
            <th>Date</th>
            <th>Time</th>
            <th>Year</th>
         </tr>
         <tr>
            <td>01/04</td>
            <td>11:25:50</td>
            <td>2012</td>
         </tr>
         <tr>
            <td>01/01</td>
            <td>1:05:20</td>
            <td>2012</td>
         </tr>
      </table>
   </body>
</html>

Explanation: Notice that we're letting the XSLT processor do most of the work. We simply apply templates at each level of the document, which moves the processing along to the children of the context node. (There's no need to explicitly loop over any elements or output any text, due to XSLT's built-in templates.) Then, we handle each table section in its own template. This is a much more flexible, maintainable, and readable approach.

Upvotes: 1

Igor Korkhov
Igor Korkhov

Reputation: 8558

This is because you're trying to select the whole contents of <Row> elements. Replacing

<td><xsl:value-of select="."/></td>

with

<td><xsl:value-of select="Date"/></td>
<td><xsl:value-of select="Time"/></td>
<td><xsl:value-of select="Year"/></td>

will fix your issue.

Upvotes: 0

Related Questions