[XSL-LIST Mailing List Archive Home] [By Thread] [By Date]

Re: [xsl] How to properly use Key elements


Subject: Re: [xsl] How to properly use Key elements
From: Emmanuel Bégué <medusis@xxxxxxxxx>
Date: Wed, 16 Oct 2013 08:25:10 +0200

Ok, there was an error in the previous solution, in that I hadn't
noticed that the first port was in the first tr (the one that contains
the ship name); so this line:
  <xsl:apply-templates select="following-sibling::tr" mode="port"/>

should in fact read:
  <xsl:apply-templates select=".|following-sibling::tr" mode="port"/>

in order to also process that first tr for the port name.

Now, there's also probably more than one ship. If each ship is in its
own table, the previous solution should work (substituting the first
match="/table" with the new root element).

If each ship is in the same table, a key can indeed be useful in order
to associate each "port tr" with a ship; the problem is to identify
the "ship tr". If we consider a "ship tr" is every tr that has more
than 3 rows, this updated solution works:

<?xml version="1.0" encoding="ISO-8859-1" ?>
<xsl:stylesheet version="1.0"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  exclude-result-prefixes="xsl">

<xsl:output method="xml" indent="yes" encoding="UTF-8"/>

<xsl:key name="portsByShip" match="tr"
use="preceding-sibling::tr[count(td) &gt; 3][1]/td[1]"/>

<xsl:template match="/root">
  <xsl:apply-templates/>
  </xsl:template>

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

<xsl:template match="thead|text()"/>

<xsl:template match="tr[count(td) &gt; 3]">
  <!-- beware, the ship name should be a valid element name (no
spaces, etc.) -->
  <xsl:variable name="shipName" select="td[1]"/>
  <xsl:element name="{$shipName}">
    <xsl:apply-templates select=".|key('portsByShip', $shipName)"
mode="port"/>
    </xsl:element>
  </xsl:template>

<xsl:template match="tr" mode="port">
  <xsl:variable name="numRows" select="count(td)"/>
  <xsl:for-each select="td[$numRows - 1]">
    <port name="{.}">
      <xsl:value-of select="following-sibling::td[1]"/>
        </port>
    </xsl:for-each>
  </xsl:template>

</xsl:stylesheet>

Applied on this input:


<?xml version="1.0" encoding="ISO-8859-1"?>
<root>
<table>
  <thead>
    <tr>
      <th>Ship</th>
      <th>Route</th>
      <th>Port</th>
      <th>Date</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td rowspan="3">Titanic</td>
      <td rowspan="2">Pacific South</td>
      <td>San Francisco</td>
      <td>dd/mm/yyyy</td>
    </tr>
    <tr>
      <td>San Diego</td>
      <td>dd/mm/yyyy</td>
    </tr>
    <tr>
      <td>Acapulco</td>
      <td>dd/mm/yyyy</td>
    </tr>
    <tr>
      <td rowspan="2">Pacific Central</td>
      <td>Acapulco</td>
      <td>dd/mm/yyyy</td>
    </tr>
    <tr>
      <td>Punteras CantC3n</td>
      <td>dd/mm/yyyy</td>
    </tr>
    <tr>
      <td>PanamC!</td>
      <td>dd/mm/yyyy</td>
    </tr>
    <tr>
      <td rowspan="3">Titanic2</td>
      <td rowspan="2">Pacific South</td>
      <td>San Francisco</td>
      <td>dd/mm/yyyy</td>
    </tr>
    <tr>
      <td>San Diego</td>
      <td>dd/mm/yyyy</td>
    </tr>
    <tr>
      <td>Acapulco</td>
      <td>dd/mm/yyyy</td>
    </tr>
    <tr>
      <td rowspan="2">Pacific Central</td>
      <td>Acapulco</td>
      <td>dd/mm/yyyy</td>
    </tr>
    <tr>
      <td>Punteras CantC3n</td>
      <td>dd/mm/yyyy</td>
    </tr>
    <tr>
      <td>PanamC!</td>
      <td>dd/mm/yyyy</td>
    </tr>
  </tbody>
</table>
</root>

produces this output:

<?xml version="1.0" encoding="UTF-8"?>
<Titanic>
   <port name="San Francisco">dd/mm/yyyy</port>
   <port name="San Diego">dd/mm/yyyy</port>
   <port name="Acapulco">dd/mm/yyyy</port>
   <port name="Acapulco">dd/mm/yyyy</port>
   <port name="Punteras CantCB3n">dd/mm/yyyy</port>
   <port name="PanamCB!">dd/mm/yyyy</port>
   <port name="San Francisco">dd/mm/yyyy</port>
</Titanic>
<Titanic2>
   <port name="San Francisco">dd/mm/yyyy</port>
   <port name="San Diego">dd/mm/yyyy</port>
   <port name="Acapulco">dd/mm/yyyy</port>
   <port name="Acapulco">dd/mm/yyyy</port>
   <port name="Punteras CantCB3n">dd/mm/yyyy</port>
   <port name="PanamCB!">dd/mm/yyyy</port>
</Titanic2>

What the "portsByShip" does, is associate every tr with the first
preceding tr that has more than 3 td child elements, and use the
content of the first td child element as the key.

Then <xsl:template match="tr[count(td) &gt; 3]"> matches each ship and
processes itself (.) and the following ones with the key.

The rule "tr[count(td) &gt; 3]" should be altered, both in the key and
the template, in order to correspond to the actual input and recognize
each ship without error.

HTH
Regards,
EB


On Wed, Oct 16, 2013 at 7:46 AM, Emmanuel BC)guC) <medusis@xxxxxxxxx> wrote:
> Hello,
>
> I don't understand what keys are supposed to achieve in this case?
>
> The following solution produces the desired output without keys:
>
> <?xml version="1.0" ?>
> <xsl:stylesheet version="1.0"
>   xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
>
> <xsl:output method="xml" indent="yes"/>
>
> <xsl:template match="/table">
>   <xsl:apply-templates/>
>   </xsl:template>
>
> <xsl:template match="*">
>   <xsl:apply-templates/>
>   </xsl:template>
>
> <xsl:template match="thead|text()"/>
>
> <xsl:template match="tr[1]">
>   <!-- beware, the ship name should be a valid element name (no
> spaces, etc.) -->
>   <xsl:variable name="shipName" select="td[1]"/>
>   <xsl:element name="{$shipName}">
>     <xsl:apply-templates select="following-sibling::tr" mode="port"/>
>     </xsl:element>
>   </xsl:template>
>
> <xsl:template match="tr" mode="port">
>   <xsl:variable name="numRows" select="count(td)"/>
>   <xsl:for-each select="td[$numRows - 1]">
>     <port name="{.}">
>       <xsl:value-of select="following-sibling::td[1]"/>
>         </port>
>     </xsl:for-each>
>   </xsl:template>
>
> </xsl:stylesheet>
>
> This may be the solution you're refering to in your last paragraph,
> and maybe the input is really more complex than the example given; but
> if the structure is similar, this should be fast enough even on a long
> input file...?
>
> Regards,
> EB
>
> On Wed, Oct 16, 2013 at 1:35 AM, G. T. Stresen-Reuter
> <tedmasterweb@xxxxxxxxx> wrote:


Current Thread