如何使用XSLT过滤XML,保留每个用户的最高sequenceNr记录
问题描述
我有一个包含用户信息的XML,每个用户单日可能有多条变更记录,由sequenceNr(序列号)标识。需要过滤后保留每个用户(按ID+startDate分组)对应的最高sequenceNr记录。
原始XML示例:
<userValues> <user> <ID>1</ID> <startDate>20241013</startDate> <someValue>200</someValue> <sequenceNr>1</sequenceNr> </user> <user> <ID>1</ID> <startDate>20241013</startDate> <someValue>500</someValue> <sequenceNr>2</sequenceNr> </user> <user> <ID>2</ID> <startDate>20241001</startDate> <someValue>200</someValue> <sequenceNr>1</sequenceNr> </user> <user> <ID>3</ID> <startDate>20241001</startDate> <someValue>200</someValue> <sequenceNr>1</sequenceNr> </user> </userValues>
期望过滤结果:
<userValues> <user> <ID>1</ID> <startDate>20241013</startDate> <someValue>500</someValue> <sequenceNr>2</sequenceNr> </user> <user> <ID>2</ID> <startDate>20241001</startDate> <someValue>200</someValue> <sequenceNr>1</sequenceNr> </user> <user> <ID>3</ID> <startDate>20241001</startDate> <someValue>200</someValue> <sequenceNr>1</sequenceNr> </user> </userValues>
我可以在XSLT处理前将XML按ID、startDate和sequenceNr排序,刚接触XSLT,想了解该需求是否可实现及具体方法。
实现方案
这个需求完全可以用XSLT实现,下面提供两种适配不同场景的方法:
方法一:利用预排序后的XML直接取每组最后一条
既然你可以提前将XML按ID→startDate→sequenceNr升序排序,那每组(相同ID+startDate)的最高sequenceNr记录就是该组的最后一条。用XSLT的分组逻辑可以轻松提取:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:output method="xml" indent="yes"/> <!-- 定义分组键:ID+startDate的组合,避免不同ID/日期的组合冲突 --> <xsl:key name="userGroup" match="user" use="concat(ID, '|', startDate)"/> <xsl:template match="/userValues"> <userValues> <!-- 筛选出每组的最后一条记录 --> <xsl:for-each select="user[generate-id() = generate-id(key('userGroup', concat(ID, '|', startDate))[last()])]"> <xsl:copy-of select="."/> </xsl:for-each> </userValues> </xsl:template> </xsl:stylesheet>
原理:通过concat(ID, '|', startDate)生成唯一分组键,用key()获取同组所有记录,取[last()]就是排序后的最高序列号记录,再用generate-id()匹配筛选出这些记录。
方法二:无需预排序,直接分组找最大sequenceNr
如果不想提前排序,也可以直接在XSLT中计算每组的最大sequenceNr,再筛选对应记录:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:output method="xml" indent="yes"/> <xsl:key name="userGroup" match="user" use="concat(ID, '|', startDate)"/> <xsl:template match="/userValues"> <userValues> <!-- 遍历每个唯一分组 --> <xsl:for-each select="user[generate-id() = generate-id(key('userGroup', concat(ID, '|', startDate))[1])]"> <!-- 获取当前分组的最大sequenceNr值 --> <xsl:variable name="maxSeq" select="max(key('userGroup', concat(ID, '|', startDate))/sequenceNr)"/> <!-- 复制对应最大序列号的记录 --> <xsl:copy-of select="key('userGroup', concat(ID, '|', startDate))[sequenceNr = $maxSeq]"/> </xsl:for-each> </userValues> </xsl:template> </xsl:stylesheet>
原理:同样用分组键分组,对每个分组计算max(sequenceNr),再筛选出该组中sequenceNr等于最大值的记录。
内容的提问来源于stack exchange,提问作者Sasku
相关产品推荐
相关产品推荐

