2

I have an issue where we need to perform a transformation of an input XHTML document in order to better display the page on mobile devices. Every input document has a multitude of HTML tables, with a specific ID. In one such table, I need to identify a cell in order to not only modify its "colspan" attribute, but also the "colspan" attributes of the cells either side of it. I cannot modify the input HTML, this is acquired externally. I can only transform it.

In every case of the cell I am trying to transform, it has a blank cell to the left and right of it, both with a "colspan = 2" attribute. I need to make it so that this middle cell has a "colspan=4" attribute, the left cell has a "colspan=1" attribute, and the right cell to be removed.

I have been using an XSLT and so far managed to achieve a number of other transforms with my document, so I know this must be possible somehow. An example table is given below.

An example of a table I have:

<html>
<body>
<table border="1">
<tr>
    <td>row 1, cell 1</td>
    <td>row 1, cell 2</td>
    <td>row 1, cell 3</td>
    <td>row 1, cell 4</td>
    <td>row 1, cell 5</td>
    </tr>
    <tr>
    <td colspan = "2">row 2, cell 1</td>
    <td>Cell I Need</td>
    <td colspan = "2">row 2, cell 3</td>
    </tr>
    <tr>
    <td>row 3, cell 1</td>
    <td>row 3, cell 2</td>
    <td>row 3, cell 3</td>
    <td>row 3, cell 4</td>
    <td>row 3, cell 5</td>
    </tr>
</table>

The table I need:

    <table border="1">
<tr>
    <td>row 1, cell 1</td>
    <td>row 1, cell 2</td>
    <td>row 1, cell 3</td>
    <td>row 1, cell 4</td>
    <td>row 1, cell 5</td>
    </tr>
    <tr>
    <td colspan = "1">row 2, cell 1</td>
    <td colspan = "4">Cell I Need</td>
    </tr>
    <tr>
    <td>row 3, cell 1</td>
    <td>row 3, cell 2</td>
    <td>row 3, cell 3</td>
    <td>row 3, cell 4</td>
    <td>row 3, cell 5</td>
    </tr>
</table>
</body>
<html>

Any suggestions on how to identify and transform this cell using XSLT would be greatly appreciated.

Many thanks, Christian

Christian
  • 167
  • 3
  • 13

2 Answers2

3

Assuming the cells in the table are empty (I am assuming the "row 2, cell 1" in your sample are purely annotations, and don't actually appear in the HTML for real), you can match a table row with the first and third cells being empty and having colspan attributes like so.

<xsl:template match="tr
   [td[1][@colspan !=''][not(text())]]
   [td[3][@colspan !=''][not(text())]]
   [not(td[4])]">

You can then combine this with the identity transform, to do extra processing on this row. Here is the full XSLT

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
   <xsl:output method="xml" indent="yes"/>

   <xsl:template match="tr[td[1][@colspan !=''][not(text())]][td[3][@colspan !=''][not(text())]][not(td[4])]">
      <xsl:copy>
         <xsl:variable name="colspan" select="sum(td/@colspan) + count(td[not(@colspan)]) - 1"/>
         <td/>
         <td colspan="{$colspan}">
            <xsl:apply-templates select="td[2]/@*|td[2]/node()"/>
         </td>
      </xsl:copy>
   </xsl:template>

   <xsl:template match="@*|node()">
      <xsl:copy>
         <xsl:apply-templates select="@*|node()"/>
      </xsl:copy>
   </xsl:template>
</xsl:stylesheet>

When applied to the following input XML

<html>
   <body>
      <table border="1">
         <tr>
            <td>row 1, cell 1</td>
            <td>row 1, cell 2</td>
            <td>row 1, cell 3</td>
            <td>row 1, cell 4</td>
            <td>row 1, cell 5</td>
         </tr>
         <tr>
            <td colspan="2"></td>
            <td>Cell I Need</td>
            <td colspan="2"></td>
         </tr>
         <tr>
            <td>row 3, cell 1</td>
            <td>row 3, cell 2</td>
            <td>row 3, cell 3</td>
            <td>row 3, cell 4</td>
            <td>row 3, cell 5</td>
         </tr>
      </table>
   </body>
</html>

The following is output

<html>
   <body>
      <table border="1">
         <tr>
            <td>row 1, cell 1</td>
            <td>row 1, cell 2</td>
            <td>row 1, cell 3</td>
            <td>row 1, cell 4</td>
            <td>row 1, cell 5</td>
         </tr>
         <tr>
            <td />
            <td colspan="4">Cell I Need</td>
         </tr>
         <tr>
            <td>row 3, cell 1</td>
            <td>row 3, cell 2</td>
            <td>row 3, cell 3</td>
            <td>row 3, cell 4</td>
            <td>row 3, cell 5</td>
         </tr>
      </table>
   </body>
</html>

Note I have allowed some flexibilty should you have more than 5 cells and the colspan be other that 2 in that case.

Also note, it is not really necessary to have colspan="1" showing. If you do have a specific requirement to include this in the output, it is not obviously not hard to do though.

Tim C
  • 70,053
  • 14
  • 74
  • 93
  • Appreciate the thorough and in depth approach, will certainly use this method if I have need to be more precise, thankyou. – Christian Dec 16 '11 at 16:56
3

In a very straightforward way:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:template match="table">
        <xsl:copy>
            <xsl:copy-of select="@*" />
            <xsl:apply-templates select="tr" />
        </xsl:copy>
    </xsl:template>
    <xsl:template match="tr">
        <tr>
            <xsl:choose>
                <xsl:when test="count(td[@colspan='2'])=2 and count(td)=3">
                    <td colspan="1">
                        <xsl:copy-of select="td[1]/node()" />
                    </td> 
                    <td colspan="4">
                        <xsl:copy-of select="td[2]/node()" />                   
                    </td>
                </xsl:when>
                <xsl:otherwise>
                    <xsl:copy-of select="td" />
                </xsl:otherwise>
            </xsl:choose>
        </tr>
    </xsl:template>
</xsl:stylesheet>
Erlock
  • 1,968
  • 10
  • 11