XML条件过滤求助:XSL处理New/Removed重复行的逻辑修正
问题描述
需要对XML数据按以下规则过滤:当两行除Status外其他所有字段完全匹配时,若一行Status为"New"、另一行为"Removed",则这两行互相抵消并移除;若某类行(比如"New")的数量多于对应的"Removed"行,多余的行需要保留。
现有逻辑在处理多行重复数据时失效:示例中行1(New)和行2(Removed)可抵消,但行3(New)无匹配的Removed行,应保留;但当前基于Key匹配的XSL代码会把所有行都移除,请求解决思路。
XML示例
<root> <row> <ID>11111</ID> <Start>01052024</Start> <End>31102024</End> <Type>BB</Type> <Status>New</Status> </row> <row> <ID>11111</ID> <Start>01052024</Start> <End>31102024</End> <Type>BB</Type> <Status>Removed</Status> </row> <row> <ID>11111</ID> <Start>01052024</Start> <End>31102024</End> <Type>BB</Type> <Status>New</Status> </row> </root>
当前XSL代码
<xsl:for-each select="row"> <xsl:variable name="concatKey" select="concat(ID, '', Start, '', End, '', Type)"/> <xsl:if test="Status = 'Removed'"> <xsl:variable name="rowMatch" select="key('groupedRows', $concatKey) [Status = 'New']"/> <xsl:if test="not($rowMatch)"> <xsl:copy-of select="."/> </xsl:if> </xsl:if> <xsl:if test="Status = 'New'"> <xsl:variable name="rowMatch" select="key('groupedRows', $concatkey) [Status = 'Removed']"/> <xsl:if test="not($rowMatch)"> <xsl:copy-of select="."/> </xsl:if> </xsl:if> </xsl:for-each>
处理思路
核心问题是当前逻辑只检查是否存在匹配的对立状态行,而没有考虑数量抵消。解决需要按分组统计数量,再保留多余的行:
- 按非Status字段分组:用
xsl:key将所有行按ID+Start+End+Type分组,确保同组内的行只有Status不同。 - 统计组内状态数量:对每个分组,分别统计
New和Removed的行数。 - 计算需保留的行数:
- 如果
New数量 >Removed数量:保留New行,数量为两者的差值 - 如果
Removed数量 >New数量:保留Removed行,数量为两者的差值 - 数量相等则全部移除
- 如果
- 输出保留的行:遍历分组,按计算出的数量输出对应状态的行。
修正后的XSL代码
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:key name="groupedRows" match="row" use="concat(ID, Start, End, Type)"/> <xsl:template match="/root"> <root> <!-- 遍历每个唯一分组 --> <xsl:for-each select="row[generate-id() = generate-id(key('groupedRows', concat(ID, Start, End, Type))[1])]"> <xsl:variable name="currentGroup" select="key('groupedRows', concat(ID, Start, End, Type))"/> <xsl:variable name="newCount" select="count($currentGroup[Status='New'])"/> <xsl:variable name="removedCount" select="count($currentGroup[Status='Removed'])"/> <!-- 保留多余的New行 --> <xsl:if test="$newCount > $removedCount"> <xsl:copy-of select="$currentGroup[Status='New'][position() <= ($newCount - $removedCount)]"/> </xsl:if> <!-- 保留多余的Removed行 --> <xsl:if test="$removedCount > $newCount"> <xsl:copy-of select="$currentGroup[Status='Removed'][position() <= ($removedCount - $newCount)]"/> </xsl:if> </xsl:for-each> </root> </xsl:template> </xsl:stylesheet>
代码说明
- 使用
generate-id()确保每个分组只处理一次,避免重复遍历。 - 统计组内两种状态的数量,计算差值后输出对应数量的行,实现精确抵消。
- 针对示例场景,分组内
New数量为2,Removed数量为1,差值为1,因此保留1条New行,符合需求。
内容的提问来源于stack exchange,提问作者user2337472
相关产品推荐
相关产品推荐

