tags:

views:

91

answers:

2

Could anyone help me with the following transformation?

Here is the XML

<Chart>
<Chart.Series>
 <DataSeries LegendText="Complete On Time"  >
  <DataSeries.DataPoints>
   <DataPoint AxisXLabel="Sep 09" YValue="10" />
   <DataPoint AxisXLabel="Oct 09" YValue="11" />
   <DataPoint AxisXLabel="Nov 09" YValue="12" />
  </DataSeries.DataPoints>
 </DataSeries>

 <DataSeries LegendText="Complete Overdue"  >
  <DataSeries.DataPoints>
   <DataPoint  YValue="1" />
   <DataPoint  YValue="2" />
   <DataPoint  YValue="3" />
  </DataSeries.DataPoints>
 </DataSeries>
</Chart.Series>
</Chart>

and here is the output id like

<table>
<thead>
 <tr>
  <th></th>
  <th>Complete On Time</th>
  <th>Complete Overdue</th>
 </tr>
</thead>
<tbody>
 <tr>
  <th>Sep 09</th>
  <th>10</th>
  <th>1</th>
 </tr>
 <tr>
  <th>Oct 09</th>
  <th>11</th>
  <th>2</th>
 </tr>
 <tr>
  <th>Nov 09</th>
  <th>12</th>
  <th>3</th>
 </tr>
</tbody>

A: 

This is something close. It's been a couple of year for me though that I've done XSLT - ignore bad style.

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" >
<xsl:output indent="yes" omit-xml-declaration="yes" ></xsl:output>
<xsl:template match="/">
 <table>
  <thead>
          <tr>
                  <th></th>
                  <xsl:apply-templates select=".//DataSeries" />
          </tr>
  </thead>
  <tbody>
   <xsl:apply-templates select="//DataSeries[1]//DataPoint">
    <xsl:with-param name="datablock">1</xsl:with-param>
   </xsl:apply-templates>
  </tbody>
 </table>
</xsl:template>
<xsl:template match="DataSeries">
 <th>
  <xsl:value-of select="@LegendText"></xsl:value-of>
 </th>
</xsl:template>
<xsl:template match="DataPoint">
 <xsl:param name="datablock" />
 <xsl:variable name="posi" select="position()"></xsl:variable>
 <xsl:if test="$datablock = 1">
  <tr>
   <xsl:for-each select="@*">
   <th>
    <xsl:value-of select="."></xsl:value-of>
   </th>
  </xsl:for-each>  
  <xsl:apply-templates select="//DataSeries[$datablock+1]//DataPoint[$posi]">
    <xsl:with-param name="datablock" select="$datablock + 1">
    </xsl:with-param>
  </xsl:apply-templates>
  </tr>
 </xsl:if>
 <xsl:if test="$datablock != 1">
  <xsl:for-each select="@*">
   <th>
    <xsl:value-of select="."></xsl:value-of>
   </th>
  </xsl:for-each>  
  <xsl:apply-templates select="//DataSeries[$datablock+1]//DataPoint[$posi]">
    <xsl:with-param name="datablock" select="$datablock + 1">
    </xsl:with-param>
  </xsl:apply-templates>
 </xsl:if>
</xsl:template>

+1  A: 

A more natural solution:

<xsl:stylesheet 
  version="1.0" 
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
>
  <!-- basic table structure -->
  <xsl:template match="Chart">
    <table>
      <thead>
        <xsl:apply-templates select="Chart.Series" mode="thead" />
      </thead>
      <tbody>
        <xsl:apply-templates select="Chart.Series" mode="tbody" />
      </tbody>
    </table>
  </xsl:template>

  <!-- table head -->
  <xsl:template match="Chart.Series" mode="thead">
    <tr>
      <th />
      <xsl:for-each select="DataSeries">
        <th>
          <xsl:value-of select="@LegendText" />
        </th>
      </xsl:for-each>  
    </tr>
  </xsl:template>

  <!-- table body -->
  <xsl:template match="Chart.Series" mode="tbody">
    <xsl:variable name="ds" select="DataSeries" />

    <!-- the first data series contains the labels -->
    <xsl:for-each select="$ds[1]/*/DataPoint">
      <xsl:variable name="pos" select="position()" />
      <tr>
        <td>
          <xsl:value-of select="@AxisXLabel" />
        </td>
        <!-- process all data points at the current position -->
        <xsl:apply-templates select="$ds/*/DataPoint[$pos]" />
      </tr>
    </xsl:for-each>
  </xsl:template>

  <!-- data points become a <td> -->
  <xsl:template match="DataPoint">
    <td>
      <xsl:value-of select="@YValue" />
    </td>
  </xsl:template>
</xsl:stylesheet>

Note that I use template modes to do different things with the same input.

The result is:

<table>
  <thead>
    <tr>
      <th />
      <th>Complete On Time</th>
      <th>Complete Overdue</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Sep 09</td>
      <td>10</td>
      <td>1</td>
    </tr>
    <tr>
      <td>Oct 09</td>
      <td>11</td>
      <td>2</td>
    </tr>
    <tr>
      <td>Nov 09</td>
      <td>12</td>
      <td>3</td>
    </tr>
  </tbody>
</table>
Tomalak
less is more! nice work
boz