如何在XSLT中将重复记录合并为单条记录?
XSLT子节点去重实现方案
需求说明
- 处理包含2条父记录的XML,每个父记录下有多层子节点
- 需按最内层
Field元素的文本值去重,同一父记录下重复值仅保留一条,唯一值正常保留
输入XML
<Records count="2"> <Record id="parentRecord"> <Record id="childRecord1"> <Record id="childRecord2"> <Field id="childRecord2Field">Technology Asset Management</Field> </Record> </Record> <Record id="childRecord1"> <Record id="childRecord2"> <Field id="childRecord2Field1">Inadequate Information Security Practices</Field> </Record> </Record> <Record id="childRecord1"> <Record id="childRecord2"> <Field id="childRecord2Field1">Inadequate Information Security Practices</Field> </Record> </Record> </Record> <Record id="parentRecord"> <Record id="childRecord1"> <Record id="childRecord2"> <Field id="childRecord2Field">Technology Asset Management</Field> </Record> </Record> <Record id="childRecord1"> <Record id="childRecord2"> <Field id="childRecord2Field1">Inadequate Information Security Practices</Field> </Record> </Record> <Record id="childRecord1"> <Record id="childRecord2"> <Field id="childRecord2Field">Technology Asset Management</Field> </Record> </Record> </Record> </Records>
期望输出XML
<Records> <Record id="parentRecord"> <Record id="childRecord1"> <Record id="childRecord2"> <Field id="childRecord2Field">Technology Asset Management</Field> </Record> </Record> <Record id="childRecord1"> <Record id="childRecord2"> <Field id="childRecord2Field1">Inadequate Information Security Practices</Field> </Record> </Record> </Record> <Record id="parentRecord"> <Record id="childRecord1"> <Record id="childRecord2"> <Field id="childRecord2Field">Technology Asset Management</Field> </Record> </Record> <Record id="childRecord1"> <Record id="childRecord2"> <Field id="childRecord2Field1">Inadequate Information Security Practices</Field> </Record> </Record> </Records>
尝试的XSLT代码
<xsl:template match="Records"> <Records> <xsl:for-each select="Record[id='parentRecord']"> <xsl:variable name="MatchedChildRecord2Value" select="Record/Record/Field[id='childRecord2Field']"> //I am trying to store all the field values in paraent node <xsl:for-each select="Record/Record/Field[id=childRecord2']/"> <xsl:choose> <xsl:when test=".=$MatchedChildRecord2Value"> //Nothing I am doing </xsl:when> <xsl:otherwise> <Record> <Record id="childRecord1"> <Record id="childRecord2"> <Field>FieldRecordvalue</Field> </Record> </Record> </xsl:otherwise> </xsl:choose> </xsl:for-each> </xsl:for-each> </Records> </xsl:template>
正确实现方案
使用Muenchian分组是XSLT中高效去重的标准方法,核心思路是按每个父记录下Field的文本值分组,仅保留每组的第一个节点,同时完整保留原XML结构。
完整XSLT代码
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <!-- 定义分组键:按父记录下Field的文本值分组 --> <xsl:key name="field-by-value" match="Record[@id='parentRecord']/Record[@id='childRecord1']" use="Record/Field/text()"/> <!-- 复制所有节点的默认模板 --> <xsl:template match="@*|node()"> <xsl:copy> <xsl:apply-templates select="@*|node()"/> </xsl:copy> </xsl:template> <!-- 处理Records节点,移除原count属性 --> <xsl:template match="Records"> <Records> <xsl:apply-templates select="@*|node()"/> </Records> </xsl:template> <!-- 处理父记录下的子节点,仅保留每组的第一个节点 --> <xsl:template match="Record[@id='parentRecord']/Record[@id='childRecord1']"> <xsl:if test="generate-id() = generate-id(key('field-by-value', Record/Field/text())[1])"> <xsl:copy> <xsl:apply-templates select="@*|node()"/> </xsl:copy> </xsl:if> </xsl:template> </xsl:stylesheet>
代码说明
- 分组键定义:
<xsl:key>定义了以Field文本值为键的分组,范围限定在每个父记录下的childRecord1节点 - 默认复制模板:保留XML的原有结构和属性,仅对需要修改的节点做特殊处理
- Records节点处理:移除原有的
count属性,生成干净的根节点 - 去重逻辑:通过
generate-id()判断当前节点是否是分组中的第一个节点,仅保留第一个节点,实现去重
内容的提问来源于stack exchange,提问作者Swap
相关产品推荐
相关产品推荐

