Build HTML table with XSL

2019-02-26 09:15发布

问题:

I'm new to XSL, trying to do something a little complicated for me, and looking for help.

Here's an example of what the XML might look like:

<foo>
  <customers>
    <customer>
      <title>Ms</title>
      <name>
        <firstName>Jane</firstName>
        <lastName>Customer</lastName>
      </name>
      <reservations>
        <reseration>
          <reservationNumber>123</reservationNumber>
          <reservationDate>2013-02-15</reservationDate>
          <table>12</table
        </reservation>
        <reseration>
          <reservationNumber>456</reservationNumber>
          <reservationDate>2013-03-24</reservationDate>
          <table>09</table
        </reservation>
        <reseration>
          <reservationNumber>789</reservationNumber>
          <reservationDate>2013-05-02</reservationDate>
          <table>22</table
        </reservation>
      </reservations>
    </customer>
    <customer>
      <title>Dr</title>
      <name>
        <firstName>John</firstName>
        <lastName>Smith</lastName>
      </name>
      <reservations>
        <reseration>
          <reservationNumber>ABC</reservationNumber>
          <reservationDate>2013-02-15</reservationDate>
          <table>05</table
        </reservation>
        <reseration>
          <reservationNumber>DEF</reservationNumber>
          <reservationDate>2013-03-24</reservationDate>
          <table>10</table
        </reservation>
        <reseration>
          <reservationNumber>GHI</reservationNumber>
          <reservationDate>2013-05-02</reservationDate>
          <table>16</table
        </reservation>
      </reservations>
    </customer>
  </customers>
</foo>

I'd like the resulting table to look like this:

Title     Name              Reservation     Date           Table
-----     ----              -----------     ----           -----
Ms        Jane Customer     123             2013-02-15     12
                            456             2013-03-24     09
                            789             2013-05-02     22
Dr        John Smith        ABC             2013-02-15     05
                            DEF             2013-03-24     10
                            GHI             2013-05-02     16

I know how to build a table this way using an XSL for-each to build each row. What's tricky about this is how to build the recursion that will pull the reservation details (reservation, date and table) from the first child element into the same row as the parent for the first row. Then only the reservation details for the remaining child elements in the remaining rows.

So this:

Title     Name              Reservation     Date           Table
-----     ----              -----------     ----           -----
Ms        Jane Customer     123             2013-02-15     12
                            456             2013-03-24     09
                            789             2013-05-02     22

Not this:

Title     Name              Reservation     Date           Table
-----     ----              -----------     ----           -----
Ms        Jane Customer     
                            123             2013-02-15     12
                            456             2013-03-24     09
                            789             2013-05-02     22

Did a lot of searches and didn't see anything quite like that.

UPDATE

Here's what I was able to get to work

<xsl:for-each select="customers/customer">
  <xsl:for-each select="./reservations/reservation">
    <xsl:choose>
      <xsl:when test="position() = 1"> 
        <tr>
          <td><xsl:value-of select="../../title"/></td> 
          <td><xsl:value-of select="reservationNumber"/></td>
        </tr>
      </xsl:when>
      ....

回答1:

The easiest way is probably to only match on reservation elements.

For each reservation, you can use <xsl:when test="node[position() > 1]"> to test whether each is the first child of its parent. If it is, you can use the parent axis to get hold of the customer details; otherwise, you can pad with blanks.



回答2:

If you want to make the Title and Name span multiple rows, you can use the count() function to find out the number of reservations.

You'll need to treat handle the first reservation differently from the rest, since the first reservation goes on the same table row.

The following fragment illustrates the idea:

  <xsl:template match="customer">
    <xsl:variable name="res" select="reservations/reservation"/>
    <tr>
      <td rowspan="{count($res)}"><xsl:value-of select="title"/></td>
      <td rowspan="{count($res)}"><xsl:apply-templates select="name"/></td>
      <!-- the first reservation goes to the same row -->
      <xsl:apply-templates select="$res[1]"/>
    </tr>
    <!-- the rest of the reservations go to their own rows -->
    <xsl:for-each select="$res[position() &gt; 1]">
      <tr>
        <xsl:apply-templates select="."/>
      </tr>
    </xsl:for-each>
  </xsl:template>

  <xsl:template match="name">
    <xsl:value-of select="concat(firstName, ' ', lastName)"/>
  </xsl:template>

  <xsl:template match="reservation">
    <td><xsl:value-of select="reservationNumber"/></td>
    <td><xsl:value-of select="reservationDate"/></td>
    <td><xsl:value-of select="table"/></td>
  </xsl:template>


标签: xslt