0
votes

I'm very new to XSL and I'm trying to build a table in the following structure: enter image description here

I am using this XML:

<CARS>
    <CAR>
        <CAR_NUM>65</CAR_NUM>
        <DRIVERS>
            <DRIVER>
                <DRIVER_NUM>123</DRIVER_NUM>
                <DRIVER_NAME>STEVE RODGERS</DRIVER_NAME>
            </DRIVER>
        </DRIVERS>
        <STATS>
            <STAT>
                <LAP>1</LAP>
                <TIME>3:21:10</TIME>
            </STAT>
            <STAT>
                <LAP>2</LAP>
                <TIME>3:21:07</TIME>
            </STAT>
        </STATS>
    </CAR>
    <CAR>
        <CAR_NUM>22</CAR_NUM>

        <DRIVERS>
            <DRIVER>
                <DRIVER_NUM>143</DRIVER_NUM>
                <DRIVER_NAME>TONY STARK</DRIVER_NAME>
            </DRIVER>
            <DRIVER>
                <DRIVER_NUM>155</DRIVER_NUM>
                <DRIVER_NAME>JAMES RHODES</DRIVER_NAME>
            </DRIVER>
        </DRIVERS>

        <STATS>
            <STAT>
                <LAP>1</LAP>
                <TIME>3:22:39</TIME>
            </STAT>
            <STAT>
                <LAP>2</LAP>
                <TIME>3:19:17</TIME>
            </STAT>
            <STAT>
                <LAP>3</LAP>
                <TIME>3:15:46</TIME>
            </STAT>
            <STAT>
                <LAP>4</LAP>
                <TIME>3:17:22</TIME>
            </STAT>
        </STATS>
    </CAR>
</CARS>

It is not possible to have more drivers than laps. The nesting of the table by CAR NUMBER is screwing everything up for me because I do not know how to generate blank fields recursively (I'm guessing) using XSL.

I know my first attempt is way off but here it is...

<table>
    <xsl:for-each select="CARS/CAR">
    <tr>
        <td><xsl:value-of select="CAR_NUM"/>
        </td>
        <xsl:for-each select="DRIVERS/DRIVER">
        <td><xsl:value-of select="DRIVER_NUM"/>
        </td>
        <td><xsl:value-of select="DRIVER_NAME"/>
        </td>
        </xsl:for-each>
        <xsl:for-each select="STATS/STAT">
        <td><xsl:value-of select="LAP"/>
        </td>
        <td><xsl:value-of select="TIME"/>
        </td>
        </xsl:for-each>
        </tr>
        </xsl:for-each>
</table>

This attempt results in:

65 123 STEVE RODGERS 1 3:21:10 2 3:21:07

22 143 TONY STARK 155 JAMES RHODES 1 3:22:39 2 3:19:17 3 3:15:46 4 3:17:22

I can't account for how the empty cells and rows will be created in the processing of looping it.

EDIT: Researching it I believe I need to analyze the XML as hierarchy tiers:

Tier 1: CAR_NUM
Tier 2: DRIVER_NUM, DRIVER_NAME
Tier 3: LAP, TIME

I need to nest a couple if's after running the first record:

T1->T2->T3 - End the row, then test for next record in T2 (if found go to T2)
T2->T3 - End the row, then test for next record in T2 (if not found, go T3)
T3
1
I made the XML well-formed. There were missing some closing tags.zx485
Thanks, I noticed that after posting and fixed it simultaneously. I'll upvote your comment for your helpfulness.alcor8
Of the 4 laps of car 22, how do you know that the driver 143 did only the first but the other driver 155 the remaining 3 laps? There doesn't seem to be any indication which driver did which laps or how a particular lap relates to a particular driver.Martin Honnen
The chart is a measurement of the car's performance. The drivers are merely the roster of who was in the car.alcor8

1 Answers

0
votes

As pointed out in a comment, I have not quite understood how to relate drivers to laps, if the relation is simply that all drivers but the last do one lap only and the last driver does all the remaining laps then you can implement that as follows:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    version="1.0">

  <xsl:output method="html" indent="yes" version="5" doctype-system="about:legacy-doctype"/>

  <xsl:template match="/">
    <html>
      <head>
        <title>.NET XSLT Fiddle Example</title>
        <style>
            th, td { vertical-align: top }
        </style>
      </head>
      <body>
          <xsl:apply-templates/>
      </body>
    </html>
  </xsl:template>

  <xsl:template match="CARS">
      <table>
          <thead>
              <tr>
                  <th>CAR NUMBER</th>
                  <th>DRIVER NUMBER</th>
                  <th>DRIVER NAME</th>
                  <th>LAP</th>
                  <th>TIME</th>
              </tr>
          </thead>
          <xsl:apply-templates select="CAR"/>
      </table>
  </xsl:template>

  <xsl:template match="CAR">
      <tbody>
          <xsl:apply-templates select="STATS/STAT"/>
      </tbody>
  </xsl:template>

  <xsl:template match="STAT">
      <tr>
          <xsl:call-template name="create-cells"/>
      </tr>
  </xsl:template>

  <xsl:template match="STAT[1]">
      <tr>
          <td rowspan="{count(../STAT)}">
              <xsl:value-of select="ancestor::CAR/CAR_NUM"/>
          </td>
          <xsl:call-template name="create-cells"/>
      </tr>
  </xsl:template>

  <xsl:template name="create-cells">
      <xsl:variable name="pos" select="position()"/>
      <xsl:apply-templates select="ancestor::CAR/DRIVERS/DRIVER[$pos]/*"/>
      <xsl:apply-templates/>      
  </xsl:template>

  <xsl:template match="DRIVER/*">
      <td>
          <xsl:apply-templates/>
      </td>
  </xsl:template>

  <xsl:template match="DRIVER[last()]/*">
      <td rowspan="{count(ancestor::CAR/STATS/STAT) - count(../DRIVER)}">
          <xsl:apply-templates/>
      </td>
  </xsl:template>

  <xsl:template match="LAP | TIME">
      <td>
          <xsl:value-of select="."/>
      </td>
  </xsl:template>

</xsl:stylesheet>

Online at https://xsltfiddle.liberty-development.net/6qVRKwy

Created HTML table seems to look as desired.