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

如何基于子元素分组复制XML且不丢失同级数据(XSLT 2.0实现)

XSLT 2.0 根据Contact的@name拆分Record并分组交易元素

看起来你需要把单个Record按照交易元素(To_Transaction和From_Transaction)里的Contact/@name拆分成多条记录,同时保留所有非交易的公共元素。你的尝试方向是对的,但分组逻辑没找对路子,我来帮你修正一下。

核心思路

我们需要:

  1. 提取所有交易中Contact/@name的唯一值,每个值对应一条新的Record
  2. 对每个唯一的name,复制原Record中的公共元素(Name、Due_Date、Customer等)
  3. 把所有属于该name的To_Transaction和From_Transaction都放到对应的新Record里

简洁的实现方案

用for-each-group直接对交易元素分组是最直观的方式,代码如下:

<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output omit-xml-declaration="yes" indent="yes"/>

  <!-- 标识模板:默认复制所有节点和属性,确保非交易元素的结构完整保留 -->
  <xsl:template match="@*|node()">
    <xsl:copy>
      <xsl:apply-templates select="@*|node()"/>
    </xsl:copy>
  </xsl:template>

  <!-- 专门处理Record节点,实现拆分逻辑 -->
  <xsl:template match="Record">
    <!-- 将所有交易元素按Contact的name属性分组 -->
    <xsl:for-each-group select="To_Transaction|From_Transaction" group-by="Contact/@name">
      <!-- 为每个分组创建新的Record节点 -->
      <Record>
        <!-- 复制原Record中所有非交易类型的子元素(公共部分) -->
        <xsl:copy-of select="../*[not(self::To_Transaction or self::From_Transaction)]"/>
        <!-- 复制当前分组下的所有交易元素 -->
        <xsl:copy-of select="current-group()"/>
      </Record>
    </xsl:for-each-group>
  </xsl:template>
</xsl:stylesheet>

代码解释

  • 标识模板:负责复制XML中大部分节点和属性,保证Name、Customer等公共元素的结构和内容完全保留,不用单独处理每个公共元素。
  • Record模板:
    • for-each-group select="To_Transaction|From_Transaction":选中当前Record下的所有交易元素
    • group-by="Contact/@name":指定按Contact的name属性作为分组依据
    • current-group():获取当前分组内的所有交易元素,直接复制到新Record中
    • ../*[not(self::To_Transaction or self::From_Transaction)]:向上找到原Record,复制所有不是交易元素的子节点(公共部分)

另一种实现方式(先获取唯一name)

如果你更喜欢先明确获取所有唯一的name值,再逐个生成Record,也可以用下面的写法,效果完全一致:

<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output omit-xml-declaration="yes" indent="yes"/>

  <xsl:template match="@*|node()">
    <xsl:copy>
      <xsl:apply-templates select="@*|node()"/>
    </xsl:copy>
  </xsl:template>

  <xsl:template match="Report">
    <xsl:copy>
      <!-- 提取所有交易中Contact@name的唯一值 -->
      <xsl:variable name="unique-names" select="distinct-values(Record/(To_Transaction|From_Transaction)/Contact/@name)"/>
      
      <!-- 遍历每个唯一name生成Record -->
      <xsl:for-each select="$unique-names">
        <xsl:variable name="current-name" select="."/>
        <Record>
          <!-- 复制公共元素 -->
          <xsl:copy-of select="/Report/Record/*[not(self::To_Transaction or self::From_Transaction)]"/>
          <!-- 筛选并复制当前name对应的To_Transaction -->
          <xsl:copy-of select="/Report/Record/To_Transaction[Contact/@name = $current-name]"/>
          <!-- 筛选并复制当前name对应的From_Transaction -->
          <xsl:copy-of select="/Report/Record/From_Transaction[Contact/@name = $current-name]"/>
        </Record>
      </xsl:for-each>
    </xsl:copy>
  </xsl:template>
</xsl:stylesheet>

你原来代码的问题

你之前的group-by="(To_Transaction,'*')"逻辑不正确,这个分组键无法正确提取交易元素的Contact/@name,而且没有针对交易元素进行分组,所以无法实现拆分效果。上面的两种写法都解决了这个问题,你可以根据自己的习惯选择。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.30 17:47:45