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

通用XSLT脚本适配缺失元素:XML转HTML表格列对齐修复

问题描述

我有一个用于生成HTML表格的XSLT脚本,生成的表格会用于邮件发送(因Outlook渲染需求包含适配斑马纹的代码)。示例XML数据源如下:

<Data>
    <Features>
        <Product caption="Product" />
        <ItemName caption="Item Name" />
        <Category caption="Category" />
        <Material caption="Material" />
        <HeightCM caption="Height"  />
        <AssignedTo caption="Category Manager" />
    </Features>
    <Product>
        <Value>001</Value>
        <ItemName>Product 1</ItemName>
        <Category>Electic</Category>
        <Material>Steel</Material>
        <HeightCM>15</HeightCM>
        <AssignedTo>James</AssignedTo>
    </Product>  
    <Product>
        <Value>002</Value>
        <ItemName>Product 2</ItemName>
        <HeightCM>12</HeightCM>
    </Product>
    <Product>
        <Value>003</Value>
        <ItemName>Product 3</ItemName>
        <Category>Sale</Category>
        <AssignedTo>Jane</AssignedTo>       
    </Product>
</Data>

该XML中,Features元素定义表头行,Product元素包含产品属性值,但并非所有Product都包含全部Features子元素对应的内容(如示例中Product 1具备所有属性,Product 2和3缺失部分属性)。

原脚本在所有Product包含全部Features数据时运行正常,但仅为存在的数据生成表格单元格,导致生成的HTML表格出现列错位问题。要求在保持脚本通用性(仅保证<Data>、<Features>、<Product>元素固定)的前提下,修改脚本以适配Product缺失Feature时所需的空表格单元格。

原XSLT脚本如下:

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
    <xsl:output method="html" indent="yes"/>
    <xsl:template match="/*">
        <html>
            <head>
                <style>
                [Removed for legibility reasons]
                </style>
            </head>
            <body>
                <table>
                    <thead>
                        <tr>
                            <xsl:apply-templates select="Features/*" mode="th"/>
                        </tr>
                    </thead>
                    <tbody>
                        <xsl:apply-templates select="*"/>
                    </tbody>
                </table>
            </body>
        </html>
    </xsl:template>
    <xsl:template match="/*/*/*" mode="th">
        <th>
            <xsl:value-of select="@caption"/>
        </th>
    </xsl:template>
    <xsl:template match="/*/*">
        <tr>
            <xsl:attribute name="class">
                <xsl:choose>
                    <xsl:when test="position() mod 2 = 0">even</xsl:when>
                    <xsl:when test="position() mod 2 = 1">odd</xsl:when>
                </xsl:choose>
            </xsl:attribute>
            <xsl:apply-templates select="*"/>
        </tr>
    </xsl:template>
    <xsl:template match="/*/*/*">
        <td>
            <xsl:value-of select="."/>
        </td>
    </xsl:template>
</xsl:stylesheet>
解决方案

核心思路是:遍历Features定义的所有表头字段,对每个Product按表头顺序逐个查找对应的子元素——存在则输出值,不存在则输出空单元格,保证每一行的单元格数量和表头列数完全匹配,避免错位。

修改后的XSLT脚本如下:

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
    <xsl:output method="html" indent="yes"/>
    
    <!-- 定义全局变量存储Features中的所有字段名称 -->
    <xsl:variable name="featureNames" select="/Data/Features/*/local-name()"/>
    
    <xsl:template match="/*">
        <html>
            <head>
                <style>
                [Removed for legibility reasons]
                </style>
            </head>
            <body>
                <table>
                    <thead>
                        <tr>
                            <xsl:apply-templates select="Features/*" mode="th"/>
                        </tr>
                    </thead>
                    <tbody>
                        <!-- 仅处理Product元素,排除Features -->
                        <xsl:apply-templates select="Product"/>
                    </tbody>
                </table>
            </body>
        </html>
    </xsl:template>
    
    <xsl:template match="/*/*/*" mode="th">
        <th>
            <xsl:value-of select="@caption"/>
        </th>
    </xsl:template>
    
    <xsl:template match="Product">
        <tr>
            <xsl:attribute name="class">
                <xsl:choose>
                    <xsl:when test="position() mod 2 = 0">even</xsl:when>
                    <xsl:when test="position() mod 2 = 1">odd</xsl:when>
                </xsl:choose>
            </xsl:attribute>
            <!-- 遍历所有feature名称,逐个生成单元格 -->
            <xsl:for-each select="$featureNames">
                <td>
                    <!-- 当前feature名称 -->
                    <xsl:variable name="currentFeature" select="."/>
                    <!-- 查找当前Product中对应的子元素,存在则取值,否则为空 -->
                    <xsl:value-of select="../*[local-name() = $currentFeature]"/>
                </td>
            </xsl:for-each>
        </tr>
    </xsl:template>
</xsl:stylesheet>

修改说明

  1. 全局变量存储字段名:新增featureNames变量,提取Features下所有子元素的名称,作为后续遍历的基准。
  2. 限定Product元素处理:原脚本中<xsl:apply-templates select="*"/>会处理所有子元素(包括Features),修改为<xsl:apply-templates select="Product"/>,避免误处理Features。
  3. 按表头顺序生成单元格:在Product的模板中,遍历featureNames变量,对每个字段查找当前Product下对应的子元素——存在则输出值,不存在则输出空<td>,确保每行单元格数量与表头一致。
  4. 保留原有斑马纹逻辑:原有的奇偶行class判断逻辑完全保留,适配Outlook的渲染需求。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.22 08:16:02