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

基于Username分组并去重HierarchyLocations的XSLT实现问题

问题描述

需要对输入XML中的<UploadUser>节点按<Username>字段分组,同时去除<HierarchyLocations>下的重复项(重复判定需同时匹配<Hierarchy>、<UserGroup>、<Location>三个字段)。现有XSLT无法实现去重,寻求正确方案。

输入XML

<Root>
  <UploadUser>
    <Username>user1</Username>
    <HierarchyLocations>
      <Hierarchy>hier1</Hierarchy>
      <UserGroup>groupA</UserGroup>
      <Location>loc1</Location>
    </HierarchyLocations>
    <HierarchyLocations>
      <Hierarchy>hier1</Hierarchy>
      <UserGroup>groupA</UserGroup>
      <Location>loc1</Location>
    </HierarchyLocations>
    <HierarchyLocations>
      <Hierarchy>hier2</Hierarchy>
      <UserGroup>groupB</UserGroup>
      <Location>loc2</Location>
    </HierarchyLocations>
  </UploadUser>
  <UploadUser>
    <Username>user1</Username>
    <HierarchyLocations>
      <Hierarchy>hier1</Hierarchy>
      <UserGroup>groupA</UserGroup>
      <Location>loc1</Location>
    </HierarchyLocations>
    <HierarchyLocations>
      <Hierarchy>hier3</Hierarchy>
      <UserGroup>groupC</UserGroup>
      <Location>loc3</Location>
    </HierarchyLocations>
  </UploadUser>
  <UploadUser>
    <Username>user2</Username>
    <HierarchyLocations>
      <Hierarchy>hier1</Hierarchy>
      <UserGroup>groupA</UserGroup>
      <Location>loc1</Location>
    </HierarchyLocations>
  </UploadUser>
</Root>

期望输出XML

<Root>
  <UploadUser>
    <Username>user1</Username>
    <HierarchyLocations>
      <Hierarchy>hier1</Hierarchy>
      <UserGroup>groupA</UserGroup>
      <Location>loc1</Location>
    </HierarchyLocations>
    <HierarchyLocations>
      <Hierarchy>hier2</Hierarchy>
      <UserGroup>groupB</UserGroup>
      <Location>loc2</Location>
    </HierarchyLocations>
    <HierarchyLocations>
      <Hierarchy>hier3</Hierarchy>
      <UserGroup>groupC</UserGroup>
      <Location>loc3</Location>
    </HierarchyLocations>
  </UploadUser>
  <UploadUser>
    <Username>user2</Username>
    <HierarchyLocations>
      <Hierarchy>hier1</Hierarchy>
      <UserGroup>groupA</UserGroup>
      <Location>loc1</Location>
    </HierarchyLocations>
  </UploadUser>
</Root>

现有错误XSLT

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:key name="userKey" match="UploadUser" use="Username"/>
  <xsl:key name="hierKey" match="HierarchyLocations" use="concat(Hierarchy, '|', UserGroup, '|', Location)"/>

  <xsl:template match="/">
    <Root>
      <xsl:for-each select="Root/UploadUser[generate-id() = generate-id(key('userKey', Username)[1])]">
        <UploadUser>
          <xsl:copy-of select="Username"/>
          <xsl:for-each select="key('userKey', Username)/HierarchyLocations">
            <xsl:if test="generate-id() = generate-id(key('hierKey', concat(Hierarchy, '|', UserGroup, '|', Location))[1])">
              <xsl:copy-of select="."/>
            </xsl:if>
          </xsl:for-each>
        </UploadUser>
      </xsl:for-each>
    </Root>
  </xsl:template>
</xsl:stylesheet>

错误输出(问题:仍存在重复的HierarchyLocations)

<Root>
  <UploadUser>
    <Username>user1</Username>
    <HierarchyLocations>
      <Hierarchy>hier1</Hierarchy>
      <UserGroup>groupA</UserGroup>
      <Location>loc1</Location>
    </HierarchyLocations>
    <HierarchyLocations>
      <Hierarchy>hier2</Hierarchy>
      <UserGroup>groupB</UserGroup>
      <Location>loc2</Location>
    </HierarchyLocations>
    <HierarchyLocations>
      <Hierarchy>hier1</Hierarchy>
      <UserGroup>groupA</UserGroup>
      <Location>loc1</Location>
    </HierarchyLocations>
    <HierarchyLocations>
      <Hierarchy>hier3</Hierarchy>
      <UserGroup>groupC</UserGroup>
      <Location>loc3</Location>
    </HierarchyLocations>
  </UploadUser>
  <UploadUser>
    <Username>user2</Username>
    <HierarchyLocations>
      <Hierarchy>hier1</Hierarchy>
      <UserGroup>groupA</UserGroup>
      <Location>loc1</Location>
    </HierarchyLocations>
  </UploadUser>
</Root>

解决方案

你之前的XSLT没成功去重,核心问题是hierKey是全局生效的——它会匹配整个文档里第一个出现的相同三元组,而不是当前用户分组内的第一个。要解决这个,得把用户名也加入去重的键里,让去重逻辑限定在单个用户的范围内。

正确的XSLT代码(XSLT 1.0)

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <!-- 按Username分组UploadUser节点 -->
  <xsl:key name="userKey" match="UploadUser" use="Username"/>
  
  <!-- 键值组合了用户名+三元组,确保去重只在当前用户的HierarchyLocations里生效 -->
  <xsl:key name="hierPerUserKey" match="HierarchyLocations" 
           use="concat(../../Username, '|', Hierarchy, '|', UserGroup, '|', Location)"/>

  <xsl:template match="/">
    <Root>
      <!-- 遍历每个唯一的用户分组 -->
      <xsl:for-each select="Root/UploadUser[generate-id() = generate-id(key('userKey', Username)[1])]">
        <UploadUser>
          <!-- 复制当前用户的Username节点 -->
          <xsl:copy-of select="Username"/>
          
          <!-- 遍历该用户所有的HierarchyLocations,只留每个三元组的第一个实例 -->
          <xsl:for-each select="key('userKey', Username)/HierarchyLocations">
            <xsl:if test="generate-id() = generate-id(key('hierPerUserKey', concat(../../Username, '|', Hierarchy, '|', UserGroup, '|', Location))[1])">
              <xsl:copy-of select="."/>
            </xsl:if>
          </xsl:for-each>
        </UploadUser>
      </xsl:for-each>
    </Root>
  </xsl:template>
</xsl:stylesheet>

逻辑说明

  1. 用户分组:用userKey把相同Username的UploadUser归为一组,每个用户只输出一个UploadUser节点。
  2. 分组内去重:新的hierPerUserKey把用户名和三个字段拼在一起当键,这样去重只会在当前用户的范围内找重复,不会跨用户干扰。
  3. 输出唯一项:遍历当前用户所有的HierarchyLocations,通过generate-id()判断是不是当前键下的第一个实例,是就保留,不是就跳过。

运行这段代码就能得到你要的期望输出,每个用户的HierarchyLocations里不会有重复的三元组了。


内容的提问来源于stack exchange,提问作者Doottu Biju

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.14 00:04:55