如何在不改变位置的前提下用XSLT去除重复ITEM段
ITEM节点按规则去重的XSLT解决方案
需求说明
移除重复的ITEM段,不改变原有节点位置,无需检查ITEM的子节点及值。现有XSLT仅检查ITEM段完全匹配,无法满足需求,还会导致ITEM字段位置错乱。
去重规则
- 多个ITEM的
N11和AR值相同时视为重复,保留任意一个; - 若同组(相同N11+AR)中存在带
subnode子节点的ITEM,则仅保留该ITEM,移除其他同组ITEM。
输入示例
<?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> </ITEM> <ITEM SEGMENT="1"> <N11>6789</N11> <AR>03</AR> <subnode> <field1> 13</field1> </subnode> </ITEM> <ITEM SEGMENT="1"> <N11>6789</N11> <AR>03</AR> </ITEM> <ITEMNEW SEGMENT="1"> <N11>6789</N11> <AR>03</AR> <PQC>NU</PQC> <QTY>3456</QTY> <NUM/> <ASCD/> </ITEMNEW> </BXYI> <BXYI SEGMENT="1"> <TNR>123453</TNR> <ORT>1000</ORT> <ITEM SEGMENT="1"> <N11>6789</N11> <AR>03</AR> </ITEM> <ITEM SEGMENT="1"> <N11>6789</N11> <AR>03</AR> <subnode> <field1> 13</field1> </subnode> </ITEM> <ITEM SEGMENT="1"> <N11>6789</N11> <AR>03</AR> </ITEM> <ITEMNEW SEGMENT="1"> <N11>6789</N11> <AR>03</AR> <PQC>NU</PQC> <QTY>3456</QTY> <NUM/> <ASCD/> </ITEMNEW> </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> <subnode> <field1> 13</field1> </subnode> </ITEM> <ITEMNEW SEGMENT="1"> <N11>6789</N11> <AR>03</AR> <PQC>NU</PQC> <QTY>3456</QTY> <NUM/> <ASCD/> </ITEMNEW> </BXYI> <BXYI SEGMENT="1"> <TNR>123453</TNR> <ORT>1000</ORT> <ITEM SEGMENT="1"> <N11>6789</N11> <AR>03</AR> <subnode> <field1> 13</field1> </subnode> </ITEM> <ITEMNEW SEGMENT="1"> <N11>6789</N11> <AR>03</AR> <PQC>NU</PQC> <QTY>3456</QTY> <NUM/> <ASCD/> </ITEMNEW> </BXYI> </BXYH> </DOC> </D02X001>
现有XSLT问题
当前使用的XSLT存在两个核心问题:
- 全局key匹配所有
BXYI/ITEM,会跨不同BXYI节点去重,违反了保留原位置的需求; - 逻辑判断未正确优先保留带
subnode的ITEM,且可能导致节点顺序错乱。
现有代码:
<?xml version="1.0" encoding="utf-8"?> <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="3.0" xmlns:xs="http://www.w3.org/2001/XMLSchema" exclude-result-prefixes="#all" expand-text="yes"> <xsl:key name="item-by-N11-AR" match="BXYI/ITEM" composite="yes" use="N11, AR"/> <xsl:template match="BXYI/ITEM[not(key('item-by-N11-AR', (N11, AR))[subnode])][not(. is key('item-by-N11-AR', (N11, AR))[1])]"/> <xsl:template match="BXYI/ITEM[key('item-by-N11-AR', (N11, AR))[subnode]][not(. is key('item-by-N11-AR', (N11, AR))[subnode][1])]"/> <xsl:mode on-no-match="shallow-copy"/> </xsl:stylesheet>
修正后的XSLT代码
<?xml version="1.0" encoding="utf-8"?> <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="3.0" xmlns:xs="http://www.w3.org/2001/XMLSchema" exclude-result-prefixes="#all" expand-text="yes"> <!-- 将key作用域限定在当前BXYI节点内,确保每组BXYI独立去重 --> <xsl:key name="item-by-N11-AR" match="ITEM" composite="yes" use="N11, AR" scope="parent::BXYI"/> <!-- 移除符合条件的重复ITEM: 1. 同组存在带subnode的ITEM时,仅保留第一个出现的带subnode的,移除其他; 2. 同组无subnode时,仅保留第一个出现的ITEM,移除其他 --> <xsl:template match="ITEM[ let $group := key('item-by-N11-AR', (N11, AR)) return ($group[subnode] and not(. is $group[subnode][1])) or (not($group[subnode]) and not(. is $group[1])) ]"/> <xsl:mode on-no-match="shallow-copy"/> </xsl:stylesheet>
代码说明
- 作用域限定:通过
scope="parent::BXYI"将key的匹配范围限制在当前BXYI节点内,避免跨节点去重; - 优先级处理:优先保留带
subnode的ITEM,若同组存在此类节点,仅保留第一个出现的; - 位置保留:仅移除重复节点,剩余节点保持原始顺序不变。
内容的提问来源于stack exchange,提问作者Padd T
相关产品推荐
相关产品推荐

