You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用XSLT展开HTML表格的rowspan与colspan以验证结构

XSLT实现HTML表格单元格展开与结构验证

核心实现思路

要完成表格单元格展开与结构验证,需分两步执行:

  • 计算表格总列数:解析表头或第一行的单元格(包含colspan属性),确定表格的基准列数
  • 逐行处理单元格:
    • 对带colspan的单元格,直接拆分为对应数量的<td>/<th>元素
    • 对带rowspan的单元格,当前行保留原单元格外,需在后续rowspan-1行中预留对应数量的空单元格占位

完整XSLT代码实现

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

    <!-- 处理整个表格,先计算总列数再逐行处理 -->
    <xsl:template match="table">
        <xsl:variable name="totalCols">
            <xsl:call-template name="calculateTotalCols">
                <xsl:with-param name="cells" select="thead/tr/th | tbody/tr[1]/td"/>
            </xsl:call-template>
        </xsl:variable>
        
        <table>
            <!-- 处理表头 -->
            <xsl:if test="thead">
                <thead>
                    <xsl:apply-templates select="thead/tr">
                        <xsl:with-param name="totalCols" select="$totalCols"/>
                        <xsl:with-param name="pendingRowspans" select="/.."/> <!-- 初始无跨行待处理 -->
                    </xsl:apply-templates>
                </thead>
            </xsl:if>
            
            <!-- 处理表体,传递跨行状态 -->
            <tbody>
                <xsl:apply-templates select="tbody/tr[1]">
                    <xsl:with-param name="totalCols" select="$totalCols"/>
                    <xsl:with-param name="pendingRowspans" select="/.."/>
                </xsl:apply-templates>
            </tbody>
            
            <!-- 输出结构验证结果 -->
            <tfoot>
                <tr>
                    <td colspan="{$totalCols}">
                        <xsl:choose>
                            <xsl:when test="count(tbody/tr[count(td) != $totalCols]) = 0">
                                *表格结构合规:所有行单元格数量与列数一致*
                            </xsl:when>
                            <xsl:otherwise>
                                *表格结构不合规:存在行单元格数量与列数不匹配*
                            </xsl:otherwise>
                        </xsl:choose>
                    </td>
                </tr>
            </tfoot>
        </table>
    </xsl:template>

    <!-- 计算表格总列数的递归模板 -->
    <xsl:template name="calculateTotalCols">
        <xsl:param name="cells"/>
        <xsl:param name="sum" select="0"/>
        
        <xsl:choose>
            <xsl:when test="$cells">
                <xsl:call-template name="calculateTotalCols">
                    <xsl:with-param name="cells" select="$cells[position() > 1]"/>
                    <xsl:with-param name="sum" select="$sum + number($cells[1]/@colspan) + (number($cells[1]/@colspan) = 0)"/>
                </xsl:call-template>
            </xsl:when>
            <xsl:otherwise>
                <xsl:value-of select="$sum"/>
            </xsl:otherwise>
        </xsl:choose>
    </xsl:template>

    <!-- 处理单行,先填充上一行遗留的跨行单元格 -->
    <xsl:template match="tr">
        <xsl:param name="totalCols"/>
        <xsl:param name="pendingRowspans"/>
        
        <xsl:variable name="currentCells" select="td | th"/>
        <xsl:variable name="processedCells">
            <!-- 填充上一行的跨行占位 -->
            <xsl:for-each select="$pendingRowspans">
                <td class="placeholder"/>
                <!-- 更新跨行剩余行数 -->
                <xsl:if test="@remaining > 1">
                    <pending remaining="{@remaining - 1}"/>
                </xsl:if>
            </xsl:for-each>
            
            <!-- 处理当前行的单元格 -->
            <xsl:apply-templates select="$currentCells">
                <xsl:with-param name="totalCols" select="$totalCols"/>
            </xsl:apply-templates>
        </xsl:variable>
        
        <!-- 补全行内不足的单元格 -->
        <tr>
            <xsl:copy-of select="$processedCells/*"/>
            <xsl:if test="count($processedCells/*) < $totalCols">
                <xsl:call-template name="fillEmptyCells">
                    <xsl:with-param name="count" select="$totalCols - count($processedCells/*)"/>
                </xsl:call-template>
            </xsl:if>
        </tr>
        
        <!-- 收集当前行产生的新跨行待处理项 -->
        <xsl:variable name="newPending">
            <xsl:for-each select="$currentCells[@rowspan and number(@rowspan) > 1]">
                <xsl:for-each select="1 to number(@rowspan) - 1">
                    <pending remaining="{number(current()/@rowspan) - position()}"/>
                </xsl:for-each>
            </xsl:for-each>
            <!-- 保留未处理完的跨行项 -->
            <xsl:copy-of select="$pendingRowspans[@remaining > 1]"/>
        </xsl:variable>
        
        <!-- 递归处理下一行 -->
        <xsl:if test="following-sibling::tr">
            <xsl:apply-templates select="following-sibling::tr[1]">
                <xsl:with-param name="totalCols" select="$totalCols"/>
                <xsl:with-param name="pendingRowspans" select="$newPending/pending"/>
            </xsl:apply-templates>
        </xsl:if>
    </xsl:template>

    <!-- 拆分带colspan的单元格 -->
    <xsl:template match="td | th">
        <xsl:param name="totalCols"/>
        <xsl:variable name="span" select="number(@colspan)"/>
        <xsl:choose>
            <xsl:when test="$span > 1">
                <xsl:call-template name="generateCells">
                    <xsl:with-param name="count" select="$span"/>
                    <xsl:with-param name="elementName" select="local-name()"/>
                    <xsl:with-param name="content" select="node()"/>
                </xsl:call-template>
            </xsl:when>
            <xsl:otherwise>
                <xsl:copy-of select="."/>
            </xsl:otherwise>
        </xsl:choose>
    </xsl:template>

    <!-- 生成指定数量的单元格模板 -->
    <xsl:template name="generateCells">
        <xsl:param name="count"/>
        <xsl:param name="elementName"/>
        <xsl:param name="content"/>
        
        <xsl:if test="$count > 0">
            <xsl:element name="{$elementName}">
                <xsl:if test="$count = 1">
                    <xsl:copy-of select="$content"/>
                </xsl:if>
            </xsl:element>
            <xsl:call-template name="generateCells">
                <xsl:with-param name="count" select="$count - 1"/>
                <xsl:with-param name="elementName" select="$elementName"/>
                <xsl:with-param name="content" select="$content"/>
            </xsl:call-template>
        </xsl:if>
    </xsl:template>

    <!-- 填充空单元格模板 -->
    <xsl:template name="fillEmptyCells">
        <xsl:param name="count"/>
        <xsl:if test="$count > 0">
            <td class="empty"/>
            <xsl:call-template name="fillEmptyCells">
                <xsl:with-param name="count" select="$count - 1"/>
            </xsl:call-template>
        </xsl:if>
    </xsl:template>
</xsl:stylesheet>

测试示例

目标HTML表格

<table>
    <thead>
        <tr>
            <th colspan="2">表头1</th>
            <th>表头2</th>
        </tr>
    </thead>
    <tbody>
        <tr>
            <td rowspan="2">跨行单元格</td>
            <td>普通单元格</td>
            <td>普通单元格</td>
        </tr>
        <tr>
            <td colspan="2">跨列单元格</td>
        </tr>
    </tbody>
</table>

转换后期望输出

<table>
    <thead>
        <tr>
            <th>表头1</th>
            <th>表头1</th>
            <th>表头2</th>
        </tr>
    </thead>
    <tbody>
        <tr>
            <td rowspan="2">跨行单元格</td>
            <td>普通单元格</td>
            <td>普通单元格</td>
        </tr>
        <tr>
            <td class="placeholder"></td>
            <td>跨列单元格</td>
            <td>跨列单元格</td>
        </tr>
    </tbody>
    <tfoot>
        <tr>
            <td colspan="3">*表格结构合规:所有行单元格数量与列数一致*</td>
        </tr>
    </tfoot>
</table>

关键说明

  • 通过pendingRowspans变量跟踪后续行需要填充的跨行单元格位置
  • 拆分colspan时仅在第一个单元格保留原内容,其余为空白占位(可按需调整内容逻辑)
  • 最终通过tfoot直观输出结构验证结果,快速判断表格是否合规

内容的提问来源于stack exchange,提问作者BWin

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.03 10:05:33