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
相关产品推荐
相关产品推荐

