1

I want to transform table like this:

<table>
  <thead>
    <tr>
        <th rowspan="3">th11</th>
        <th>th12</th>
        <th rowspan="2">th13</th>
        <th colspan="2">th14</th>
    </tr>
    <tr>
        <th>th21</th>
        <th>th22</th>
        <th>th23</th>
    </tr>
    <tr>
        <th>th31</th>
        <th>th32</th>
        <th>th33</th>
        <th>th34</th>
    </tr>
  </thead>
</table>

to this table:

<table>
  <thead>
    <tr>
        <th rowspan="3">th11</th>
        <th>th12</th>
        <th rowspan="2">th13</th>
        <th colspan="2">th14</th>
    </tr>
    <tr>
        <th></th>
        <th>th21</th>
        <th></th>
        <th>th22</th>
        <th>th23</th>
    </tr>
    <tr>
        <th></th>
        <th>th31</th>
        <th>th32</th>
        <th>th33</th>
        <th>th34</th>
    </tr>
  </thead>
</table>

In fact I want to put empty cells below cells with rowspan, according to rowspan number. cells' rowspan and colspan attributes should be appeared in final result.

<xsl:for-each select="tr">
        <xsl:value-of select="sum(th[@colspan]/@colspan) + count(th[not(@colspan)])"/>
</xsl:for-each>

I want this code to give me the same numbers.

How can I do this using XSLT? What is the appropriate algorithm for this purpose? Any help would be appreciated.

  • See http://andrewjwelch.com/code/xslt/table/table-normalization.html for instance, I also think we had similar questions and solutions using above link here on stackoverflow, like http://stackoverflow.com/questions/36103851/convert-html-colspan-and-rowspan-to-empty-cells. – Martin Honnen Jun 05 '16 at 08:25
  • @MartinHonnen Thank you so much for your response. But how can we do just for rowspan with generated empty cells? Algorithm is very complicated! – saeed asalisaf Jun 05 '16 at 09:33
  • @saeedasalisaf, I cannot understand what is wanted -- don't see any unambiguous rule for transforming the provided XML document into the provided result. Could you, please, edit the question and explain better? Maybe replacing the ambiguous "text" with something unique would also help trace what input goes to what output. – Dimitre Novatchev Jun 05 '16 at 18:15
  • @saeedasalisaf, In particular, could you, please, provide the expected result in case the 2nd `` with `rowspan` attribute was `text`? – Dimitre Novatchev Jun 05 '16 at 18:51
  • @DimitreNovatchev Thank you Dimitre. I edited my code and explanation. – saeed asalisaf Jun 07 '16 at 06:58

1 Answers1

1

An adaption (removing the code for dealing with colspan but adding code for dealing with thead) of the XSLT 2.0 solution posted in Convert HTML colspan and rowspan to "empty" cells is as follows

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xs="http://www.w3.org/2001/XMLSchema" version="2.0" exclude-result-prefixes="xs">

    <xsl:output indent="yes" omit-xml-declaration="yes" />

    <xsl:variable name="table_with_no_rowspans">
        <xsl:apply-templates mode="rowspan"/>
    </xsl:variable>

    <xsl:template match="/">
        <xsl:apply-templates select="$table_with_no_rowspans/*" mode="final" />
    </xsl:template>

    <xsl:template match="@*|*" mode="#all">
        <xsl:copy>
            <xsl:apply-templates select="@*|*" mode="#current" />
        </xsl:copy>
    </xsl:template>

    <!-- make sure it works for both table/tr and table/tbody/tr and table/thead/tr -->
    <xsl:template match="table/tbody | table[not(tbody | thead)] | table/thead" mode="rowspan">
        <xsl:copy>
            <xsl:copy-of select="tr[1]" />
            <xsl:apply-templates select="tr[2]" mode="rowspan">
                <xsl:with-param name="previousRow" select="tr[1]" />
            </xsl:apply-templates>
        </xsl:copy>
    </xsl:template>

    <xsl:template match="tr" mode="rowspan">
        <xsl:param name="previousRow" as="element()" />

        <xsl:variable name="currentRow" select="." />

        <xsl:variable name="normalizedTDs">
            <xsl:for-each select="$previousRow/*">
                <xsl:choose>
                    <xsl:when test="@rowspan &gt; 1">
                        <xsl:copy>
                            <xsl:attribute name="rowspan">
                                <xsl:value-of select="@rowspan - 1" />
                            </xsl:attribute><!--
                            <xsl:copy-of select="@*[not(name() = 'rowspan')]" />
                            <xsl:copy-of select="node()" />
                        --></xsl:copy>
                    </xsl:when>
                    <xsl:otherwise>
                        <xsl:copy-of select="$currentRow/*[1 + count(current()/preceding-sibling::*[not(@rowspan) or (@rowspan = 1)])]" />
                    </xsl:otherwise>
                </xsl:choose>
            </xsl:for-each>
        </xsl:variable>

        <xsl:variable name="newRow" as="element(tr)">
            <xsl:copy>
                <xsl:copy-of select="$currentRow/@*" />
                <xsl:copy-of select="$normalizedTDs" />
            </xsl:copy>
        </xsl:variable>

        <xsl:copy-of select="$newRow" />

        <xsl:apply-templates select="following-sibling::tr[1]" mode="rowspan">
            <xsl:with-param name="previousRow" select="$newRow" />
        </xsl:apply-templates>
    </xsl:template>

    <xsl:template match="td | th" mode="final">
        <xsl:choose>
            <xsl:when test="@rowspan">
                <xsl:copy>
                    <xsl:copy-of select="@* except @rowspan" />
                    <xsl:copy-of select="node()" />
                </xsl:copy>
            </xsl:when>
            <xsl:otherwise>
                <xsl:copy-of select="." />
            </xsl:otherwise>
        </xsl:choose>
    </xsl:template>

</xsl:stylesheet>
Community
  • 1
  • 1
Martin Honnen
  • 160,499
  • 6
  • 90
  • 110