Sorry I don't know how that happened, what's below should be easier to read
On 1/10/2026 03:23, Trevor Nicholls trevor@xxxxxxxxxxxxxxxxxx wrote:
So I took Andrew's stylesheet and tested it in my environment, but
unfortunately I have a couple of problems with it. One I think I can
solve, the other one I do not understand.
Here's the stylesheet, unedited:
<!-- Table Normalization - HT Andrew Welch -->
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema" exclude-result-prefixes="xs">
\xA0 \xA0 \xA0 \xA0 <xsl:output method="xml" encoding="UTF-8"/>
\xA0 \xA0 \xA0 \xA0 <xsl:variable name="table_with_no_colspans">
\xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 <xsl:apply-templates mode="colspan"/>
\xA0 \xA0 \xA0 \xA0 </xsl:variable>
\xA0 \xA0 \xA0 \xA0 <xsl:variable name="table_with_no_rowspans">
\xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 <xsl:for-each select="$table_with_no_colspans">
\xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 <xsl:apply-templates mode="rowspan"/>
\xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 </xsl:for-each>
\xA0 \xA0 \xA0 \xA0 </xsl:variable>
\xA0 \xA0 \xA0 \xA0 <xsl:template match="/">
\xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 <xsl:apply-templates select="$table_with_no_rowspans"
mode="final"/>
\xA0 \xA0 \xA0 \xA0 </xsl:template>
\xA0 \xA0 \xA0 \xA0 <xsl:template match="@*|*" mode="#all">
\xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 <xsl:copy>
<!-- Note 2 -->
\xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 <xsl:apply-templates select="@*|*"
mode="#current"/>
\xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 </xsl:copy>
\xA0 \xA0 \xA0 \xA0 </xsl:template>
\xA0 \xA0 \xA0 \xA0 <xsl:template match="td" mode="colspan">
\xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 <xsl:choose>
\xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 <xsl:when test="@colspan">
\xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 <xsl:variable name="this" select="."
as="element()"/>
\xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 <xsl:for-each select="1 to @colspan">
\xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 <td>
\xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 <xsl:copy-of
select="$this/@*[not(name() = 'colspan')][not(name() = 'width')]"/>
\xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 <xsl:copy-of
select="$this/node()"/>
\xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 </td>
\xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 </xsl:for-each>
\xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 </xsl:when>
\xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 <xsl:otherwise>
\xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 <xsl:copy-of select="."/>
\xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 </xsl:otherwise>
\xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 </xsl:choose>
\xA0 \xA0 \xA0 \xA0 </xsl:template>
\xA0 \xA0 \xA0 \xA0 <xsl:template match="tbody" mode="rowspan">
\xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 <xsl:copy>
\xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 <xsl:copy-of select="tr[1]"/>
\xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 <xsl:apply-templates select="tr[2]"
mode="rowspan">
\xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 <xsl:with-param name="previousRow"
select="tr[1]"/>
\xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 </xsl:apply-templates>
\xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 </xsl:copy>
\xA0 \xA0 \xA0 \xA0 </xsl:template>
\xA0 \xA0 \xA0 \xA0 <xsl:template match="tr" mode="rowspan">
<!-- Note 1 -->
\xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 <xsl:param name="previousRow" as="node()"/>
\xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 <xsl:variable name="currentRow" select="."/>
\xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 <xsl:variable name="normalizedTDs">
\xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 <xsl:for-each select="$previousRow/td">
\xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 <xsl:choose>
\xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 <xsl:when test="@rowspan > 1">
\xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 <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()"/>
\xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 </xsl:copy>
\xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 </xsl:when>
\xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 <xsl:otherwise>
\xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 <xsl:copy-of
select="$currentRow/td[1 +
count(current()/preceding-sibling::td[not(@rowspan) or (@rowspan =
1)])]"/>
\xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 </xsl:otherwise>
\xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 </xsl:choose>
\xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 </xsl:for-each>
\xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 </xsl:variable>
\xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 <xsl:variable name="newRow" as="element(tr)">
\xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 <xsl:copy>
\xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 <xsl:copy-of select="$currentRow/@*"/>
\xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 <xsl:copy-of select="$normalizedTDs"/>
\xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 </xsl:copy>
\xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 </xsl:variable>
\xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 <xsl:copy-of select="$newRow"/>
\xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 <xsl:apply-templates select="following-sibling::tr[1]"
mode="rowspan">
\xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 <xsl:with-param name="previousRow"
select="$newRow"/>
\xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 </xsl:apply-templates>
\xA0 \xA0 \xA0 \xA0 </xsl:template>
\xA0 \xA0 \xA0 \xA0 <xsl:template match="td" mode="final">
\xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 <xsl:choose>
\xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 <xsl:when test="@rowspan">
\xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 <xsl:copy>
\xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 <xsl:copy-of
select="@*[not(name() = 'rowspan')]"/>
\xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 <xsl:copy-of select="node()"/>
\xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 </xsl:copy>
\xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 </xsl:when>
\xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 <xsl:otherwise>
\xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 <xsl:copy-of select="."/>
\xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 </xsl:otherwise>
\xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 \xA0 </xsl:choose>
\xA0 \xA0 \xA0 \xA0 </xsl:template>
</xsl:stylesheet>
<!-- end of Andrew's stylesheet -->
When I try to run this transform across one of my documents (using
Saxon) I get this error:
Error at xsl:param on line 56 of file: (path-to-xsl):
\xA0 \xA0 XTDE0610: A value must be supplied for the parameter because the
default value is not a valid instance of the required type
If I remove the as="node()" from the previousRow param at Note 1 above
I get a different error:
Error on line 61 of file:
\xA0 \xA0 XPTY0019: Required item type of first operand of '/' is node();
supplied value has item type xs:string
The default value is kind of academic because so far as I can see this
template is always called with-param, so I tried replacing the line with
\xA0 \xA0<xsl:param name="previousRow" as="node()?"/>
and the transform then runs. Unfortunately it only fixes the runtime
error, it doesn't actually generate sensible output.
My second problem is that the output of this transformation contains
the XML structure but it has lost all the content.
To fix that I had to add "text()" to the apply-templates at Note 2.
Now most of the document is preserved, but it's clear that every
single <tr> in the output document is empty. This tells me that the
fix at Note 1 may simply be masking a more serious problem.
Is there enough in the foregoing for somebody to see what might be
wrong in my/Andrew's stylesheet?
Thanks
T
On 29/09/2026 02:52, Martin Honnen martin.honnen@xxxxxx wrote:
On 28/09/2026 15:47, Trevor Nicholls trevor@xxxxxxxxxxxxxxxxxx wrote:
(note: I'm looking for an XSL2 solution at this point.)
https://ajwelch.blogspot.com/2006/09/table-normalization-in-xslt-20.html
|