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

Groovy迭代实现XML按Header为X标记分组聚合Entry方案

SAP CPI 场景下XML分组聚合转换实现方案

核心逻辑

原有逐Entry生成<AtlasFX_row>的逻辑问题在于没有维护分组上下文,正确遍历逻辑遵循以下规则:

  • 遍历全量<Entry>节点时,单独维护一个当前分组的引用变量,不要为每个Entry直接生成根级行节点
  • 检测到当前Entry下<Header>节点去空格后值为X时,判定为新业务分组起点:新建<AtlasFX_row>节点挂载到输出根节点,将当前Entry的公共头字段(CompanyCode、EntryType等)写入该节点,同时把当前分组引用指向这个新建的行节点;该Entry自身的明细字段也要作为第一个<Item>子节点写入当前分组
  • 检测到当前Entry下<Header>节点值为空时,不新建行节点,仅在当前持有的分组引用节点下追加<Item>子节点,写入当前Entry的明细字段即可
  • 遍历全程不提前输出节点,所有节点结构组装完成后统一序列化输出

可直接部署的Groovy脚本

import com.sap.gateway.ip.core.customdev.util.Message
import groovy.xml.XmlUtil

def Message processData(Message message) {
    // 读取入站XML内容
    String inputXml = message.getBody(String)
    // 如输入带XML命名空间,请在XmlParser构造时配置命名空间映射
    Node inputRoot = new XmlParser().parseText(inputXml)
    // 定义输出根节点,可根据实际接口要求替换节点名
    Node outputRoot = new Node(null, "MessageRoot")

    // 持有当前业务分组的节点引用
    Node currentGroupNode = null

    inputRoot.Entry.each { entryNode ->
        String headerMark = entryNode.Header?.text()?.trim()
        if (headerMark == "X") {
            // 新建分组行节点
            currentGroupNode = new Node(outputRoot, "AtlasFX_row")
            // 写入公共头字段,以下字段请根据实际输入结构增删调整
            currentGroupNode.appendNode("CompanyCode", entryNode.CompanyCode?.text()?.trim())
            currentGroupNode.appendNode("EntryType", entryNode.EntryType?.text()?.trim())
            currentGroupNode.appendNode("DocumentNumber", entryNode.DocumentNumber?.text()?.trim())
            currentGroupNode.appendNode("PostingDate", entryNode.PostingDate?.text()?.trim())
            currentGroupNode.appendNode("Currency", entryNode.Currency?.text()?.trim())

            // 写入分组首条明细
            Node firstItem = new Node(currentGroupNode, "Item")
            firstItem.appendNode("GLAccount", entryNode.GLAccount?.text()?.trim())
            firstItem.appendNode("DebitAmount", entryNode.DebitAmount?.text()?.trim() ?: "0")
            firstItem.appendNode("CreditAmount", entryNode.CreditAmount?.text()?.trim() ?: "0")
            firstItem.appendNode("CostCenter", entryNode.CostCenter?.text()?.trim())
        } else {
            // 非分组起点,追加明细到当前分组,空引用判断用于过滤无归属的脏数据
            if (currentGroupNode != null) {
                Node itemNode = new Node(currentGroupNode, "Item")
                itemNode.appendNode("GLAccount", entryNode.GLAccount?.text()?.trim())
                itemNode.appendNode("DebitAmount", entryNode.DebitAmount?.text()?.trim() ?: "0")
                itemNode.appendNode("CreditAmount", entryNode.CreditAmount?.text()?.trim() ?: "0")
                itemNode.appendNode("CostCenter", entryNode.CostCenter?.text()?.trim())
            }
        }
    }

    // 序列化XML写回消息体
    message.setBody(XmlUtil.serialize(outputRoot))
    return message
}

适配调整说明

  • 若输入XML带命名空间,实例化XmlParser时可传入new XmlParser(false, false)关闭命名空间校验,或提前声明命名空间映射,避免节点取值为空
  • 脚本中列出的头字段、明细字段仅为示例,必须和实际输入XML的节点名保持一致,不需要的字段直接删除对应appendNode行即可
  • 若需要输出XML声明头(<?xml version="1.0" encoding="UTF-8"?>),可替换序列化逻辑自定义输出格式和编码
  • 若输入存在末尾无X标记的零散Entry,脚本会自动将其归入最后一个有效分组,符合业务规则

结果校验标准

  • 输出结构中<AtlasFX_row>的总数量,和输入中<Header>值为X的Entry数量完全相等,不会出现空Header对应的无效行节点
  • 每个<AtlasFX_row>下的头字段取值,和对应分组第一个标记X的Entry字段值完全一致,不会被后续Entry的字段覆盖
  • 每个<AtlasFX_row>下的<Item>数量,等于该分组起点到下一个X标记Entry之间的所有Entry总数,无遗漏无重复

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.02 07:01:10