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

如何对同一XML表格应用多组XSLT 2.0转换?

问题

处理客户提供的不规范XML文档时,需要用XSLT 2.0完成两个转换:

  1. 移除表格中的空<row/>元素
  2. 对包含表头行(含outputclass="TBL_Heading"的<p>)的表格,将其outputclass从OriginalTableClass改为NewTableClass

单独执行两个转换均正常,但同时使用时空行无法被移除。

XML示例片段:

<table outputclass="OriginalTableClass">
    <tgroup cols="2">
        <tbody>
            <row>
                <entry>
                    <p outputclass="TBL_Heading">Heading</p>
                </entry>
                <entry>
                    <p outputclass="TBL_Heading">Heading</p>
                </entry>
            </row>
            <row>
                <entry morerows="1">
                    <p outputclass="TBL_Body_Text">Content</p>
                </entry>
                <entry>
                    <p outputclass="TBL_Body_Text">Content</p>
                </entry>
            </row>
            <row/>
            <row/>
        </tbody>
    </tgroup>
</table>

最初的XSLT代码(单独移除空行有效):

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    xmlns:x2w="http://www.orbistechnologies.com/xml2word"
    exclude-result-prefixes="xs"
    version="2.0">
    
    <xsl:template match="*" mode="ABC">
        <xsl:copy>
            <xsl:copy-of select="@*"/>
            <xsl:apply-templates mode="ABC"/>
        </xsl:copy>
    </xsl:template>
    
    <!-- 移除空行 -->
    <xsl:template match="tbody/row[not(child::*)]" mode="ABC"/>
</xsl:stylesheet>

添加修改表格outputclass的模板后(单独有效,合并后空行无法移除):

<!-- 修改含表头表格的outputclass -->
<xsl:template match="table[@outputclass='OriginalTableClass']" mode="ABC">
    <xsl:copy>
        <xsl:element name="table">
            <xsl:choose>
                <xsl:when test="*/descendant::p[@outputclass='TBL_Heading']">
                    <xsl:attribute name="outputclass">NewTableClass</xsl:attribute>
                </xsl:when>
                <xsl:otherwise>
                    <xsl:attribute name="outputclass">
                        <xsl:value-of select="@outputclass"/>
                    </xsl:attribute>
                </xsl:otherwise>
            </xsl:choose>
            <!-- 复制表格内部内容 -->
            <xsl:copy-of select="./node()"/>
        </xsl:element>
    </xsl:copy>
</xsl:template>
核心问题分析

问题出在表格模板里的<xsl:copy-of select="./node()"/>——这个指令是直接复制原表格的所有子节点,完全跳过了后续的模板匹配流程。

XSLT的核心逻辑是模板匹配+递归处理:当你用<xsl:apply-templates>时,会触发对当前节点子元素的模板匹配(包括移除空行的模板);但<xsl:copy-of>只是原样复制节点,不会执行任何转换逻辑,所以空行被原封不动地保留下来,根本没机会触发移除模板。

另外,表格模板里多余的<xsl:copy>+新<table>元素嵌套,会导致输出XML出现重复的<table>标签,反而破坏结构。

修正方案

修改表格模板,把<xsl:copy-of>替换为<xsl:apply-templates mode="ABC"/>,同时简化表格元素的复制逻辑:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    xmlns:x2w="http://www.orbistechnologies.com/xml2word"
    exclude-result-prefixes="xs"
    version="2.0">
    
    <!-- 通用复制模板(身份模板) -->
    <xsl:template match="*" mode="ABC">
        <xsl:copy>
            <xsl:copy-of select="@*"/>
            <xsl:apply-templates mode="ABC"/>
        </xsl:copy>
    </xsl:template>
    
    <!-- 移除空行模板 -->
    <xsl:template match="tbody/row[not(child::*)]" mode="ABC"/>
    
    <!-- 修改含表头表格的outputclass模板 -->
    <xsl:template match="table[@outputclass='OriginalTableClass']" mode="ABC">
        <xsl:copy>
            <!-- 处理outputclass属性 -->
            <xsl:choose>
                <xsl:when test="*/descendant::p[@outputclass='TBL_Heading']">
                    <xsl:attribute name="outputclass">NewTableClass</xsl:attribute>
                </xsl:when>
                <xsl:otherwise>
                    <xsl:copy-of select="@outputclass"/>
                </xsl:otherwise>
            </xsl:choose>
            <!-- 递归处理表格内部子节点,触发空行移除模板 -->
            <xsl:apply-templates mode="ABC"/>
        </xsl:copy>
    </xsl:template>
</xsl:stylesheet>

关键改动说明:

  1. 用<xsl:copy>直接复制原<table>元素,避免嵌套问题
  2. 把<xsl:copy-of select="./node()"/>换成<xsl:apply-templates mode="ABC"/>,让表格内部的<tgroup>、<tbody>、<row>等节点都能触发对应的转换模板,包括空行移除逻辑
  3. 简化属性处理:用<xsl:copy-of select="@outputclass"/>代替重新创建属性,更简洁高效
后续扩展建议

当需要添加更多转换逻辑时,遵循以下原则即可保证所有转换生效:

  • 始终用<xsl:apply-templates>代替<xsl:copy-of>处理子节点:只有这样才能让子节点触发对应的转换模板,实现链式处理
  • 保留身份模板的基础地位:通用的身份模板(匹配所有元素并复制)是XSLT的基础,所有特殊模板(如修改表格、移除空行)都是对它的补充,优先级更高的特殊模板会覆盖身份模板的处理
  • 明确模板优先级:如果多个模板匹配同一节点,可以通过priority属性指定优先级(数值越高优先级越高),避免匹配歧义
  • 分组管理模板:按功能把模板分组(比如表格处理、文本处理)并添加注释,方便后续维护

内容的提问来源于stack exchange,提问作者Randy H in CT

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.20 19:07:06