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

XSLT通用移除子元素重复的ITEM段方案咨询及代码排查

XSLT实现通用去重重复ITEM节点及现有代码排查

问题描述

需要编写XSLT删除BXYI下子节点值完全相同的重复ITEM段。当前ITEM包含6个字段,但后续可能新增空标签或带值字段,希望以通用方式实现去重逻辑。同时有两个疑问:

  1. 是否可以用preceding-sibling函数查找并移除重复ITEM?
  2. 是否必须定义所有可能的字段才能实现去重?
    另外需要排查当前使用的XSLT代码未生效的原因。

输入示例

<?xml version="1.0" encoding="UTF-8"?>
<D02X001>
    <DOC BEGIN="1">
        <DC40 SEGMENT="1">
            <NAM>DC40</NAM>     
        </DC40>
        <BXYH SEGMENT="1">
            <LDAT>date</LDAT>
            <UDAT>date1</UDAT>          
            <BXYI SEGMENT="1">
                <TNR>123453</TNR>           
                <ORT>1000</ORT>
                
                <ITEM SEGMENT="1">
                    <N11>6789</N11>
                    <AR>03</AR>
                    <PQC>NU</PQC>
                    <QTY>90909</QTY>
                    <NUM/>
                    <ASCD/>
                </ITEM>
                <ITEM SEGMENT="1">
                    <N11>6789</N11>
                    <AR>03</AR>
                    <PQC>NU</PQC>
                    <QTY>3456</QTY>
                    <NUM/>
                    <ASCD/>
                </ITEM>
                <ITEM SEGMENT="1">
                    <N11>6789</N11>
                    <AR>03</AR>
                    <PQC>NU</PQC>
                    <QTY>3456</QTY>
                    <NUM/>
                    <ASCD/>
                </ITEM>
                <ITEM SEGMENT="1">
                    <N11>6789</N11>
                    <AR>03</AR>
                    <PQC>NU</PQC>
                    <QTY>3456</QTY>
                    <NUM/>
                    <ASCD/>
                </ITEM>
            </BXYI>
        </BXYH>
    </DOC>
</D02X001>

输出示例

<?xml version="1.0" encoding="UTF-8"?>
<D02X001>
    <DOC BEGIN="1">
        <DC40 SEGMENT="1">
            <NAM>DC40</NAM>     
        </DC40>
        <BXYH SEGMENT="1">
            <LDAT>date</LDAT>
            <UDAT>date1</UDAT>          
            <BXYI SEGMENT="1">
                <TNR>123453</TNR>           
                <ORT>1000</ORT>
                
                <ITEM SEGMENT="1">
                    <N11>6789</N11>
                    <AR>03</AR>
                    <PQC>NU</PQC>
                    <QTY>90909</QTY>
                    <NUM/>
                    <ASCD/>
                </ITEM>
                <ITEM SEGMENT="1">
                    <N11>6789</N11>
                    <AR>03</AR>
                    <PQC>NU</PQC>
                    <QTY>3456</QTY>
                    <NUM/>
                    <ASCD/>
                </ITEM>     
            </BXYI>
        </BXYH>
    </DOC>
</D02X001>

现有XSLT代码

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:key name="uniqueItems" match="ITEM" use="concat(N11, '-', AR, '-', PQC, '-', QTY)"/>
    <xsl:output method="xml" indent="yes"/>
    <xsl:template match="/">
        <xsl:copy-of select="."/>
    </xsl:template>
    <xsl:template match="BXYH">
        <xsl:copy>
            <xsl:apply-templates select="*"/>
            <BXYI SEGMENT="1">
                <xsl:for-each select="ITEM">
                    <xsl:if test="generate-id() = generate-id(key('uniqueItems', concat(N11, '-', AR, '-', PQC, '-', QTY))[1])">
                        <xsl:copy-of select="."/>
                    </xsl:if>
                </xsl:for-each>
            </BXYI>
        </xsl:copy>
    </xsl:template>
</xsl:stylesheet>

现有代码未生效原因排查

你的XSLT代码存在三个关键问题:

  1. 根模板优先级冲突:匹配/的模板直接用xsl:copy-of复制整个文档,后续匹配BXYH的模板根本不会执行,因为根模板已经处理了所有内容。
  2. 节点路径错误:BXYH模板中select="ITEM"是直接查找BXYH下的ITEM,但实际ITEM嵌套在BXYI内部,正确路径应为BXYI/ITEM。
  3. 错误重建节点:代码在BXYH模板中重新生成了BXYI节点,会丢失原BXYI下的TNR、ORT等节点,同时导致BXYI重复。

通用去重方案

无需提前定义所有字段,以下两种方式均可实现通用去重:

方法1:使用xsl:key(推荐,性能更优)

利用xsl:key将ITEM的所有子节点内容拼接为键值,自动适配新增字段。XSLT 1.0实现如下:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <!-- 以ITEM所有子节点的规范化文本拼接作为去重键 -->
    <xsl:key name="uniqueItemKey" match="ITEM" use="concat(*/normalize-space(), '|')"/>
    
    <xsl:output method="xml" indent="yes" encoding="UTF-8"/>
    
    <!-- 身份模板:默认复制所有节点和属性 -->
    <xsl:template match="@*|node()">
        <xsl:copy>
            <xsl:apply-templates select="@*|node()"/>
        </xsl:copy>
    </xsl:template>
    
    <!-- 移除重复的ITEM:仅保留每个唯一ITEM的第一个实例 -->
    <xsl:template match="ITEM[generate-id() != generate-id(key('uniqueItemKey', concat(*/normalize-space(), '|'))[1])]"/>
</xsl:stylesheet>

concat(*/normalize-space(), '|')会自动将ITEM下所有子节点的文本(去除多余空格后)拼接为键,新增字段无需修改代码。

方法2:使用preceding-sibling

通过preceding-sibling检查当前ITEM之前是否存在完全相同的节点,适合小文档场景:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output method="xml" indent="yes" encoding="UTF-8"/>
    
    <!-- 身份模板:默认复制所有节点和属性 -->
    <xsl:template match="@*|node()">
        <xsl:copy>
            <xsl:apply-templates select="@*|node()"/>
        </xsl:copy>
    </xsl:template>
    
    <!-- 移除有完全相同前置兄弟的ITEM -->
    <xsl:template match="ITEM[preceding-sibling::ITEM[
        count(*) = current()/count(*) and
        every $child in * satisfies 
            exists(current()/*[name()=name($child) and normalize-space(.)=normalize-space($child)])
    ]]"/>
</xsl:stylesheet>

逻辑说明:如果当前ITEM的前置兄弟中存在一个ITEM,两者子节点数量相同,且每个子节点的名称和规范化文本完全一致,则移除当前ITEM。

关键说明

  • 无需定义所有字段:两种方案都能自动适配ITEM的新增字段,无需修改代码。
  • 性能差异:xsl:key方式预构建键值,处理大量重复节点时性能更优;preceding-sibling需逐个节点比较,大文档场景效率较低。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.16 06:54:56