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

基于Groovy多级Map递归生成带属性与子节点的XML方案问询

嘿,我刚好折腾过类似的需求!用Groovy的MarkupBuilder来递归处理这种每层都带attributes和elements的嵌套Map生成XML,简直是量身定做的方案,比手动拼接字符串舒服太多了。

我直接给你上可运行的代码示例,再拆解关键点:

第一步:定义你的嵌套Map结构

先模拟一个符合你要求的多层数据结构(你可以直接替换成自己的真实数据):

def xmlData = [
    // 顶层(根节点)的属性
    attributes: [xmlVersion: "1.0", lang: "en"],
    // 根节点下的子元素列表
    elements: [
        [
            name: "user",
            attributes: [id: "1001", status: "active"],
            elements: [
                [name: "name", text: "John Doe"],
                [name: "contact", attributes: [type: "email"], text: "john@example.com"],
                [
                    name: "address",
                    attributes: [zip: "90210"],
                    elements: [
                        [name: "street", text: "123 Main St"],
                        [name: "city", text: "Los Angeles"]
                    ]
                ]
            ]
        ],
        [
            name: "user",
            attributes: [id: "1002", status: "inactive"],
            elements: [
                [name: "name", text: "Jane Smith"]
            ]
        ]
    ]
]

第二步:编写递归构建XML的函数

核心就是利用MarkupBuilder的闭包特性,递归遍历每个层级的元素:

import groovy.xml.MarkupBuilder

def buildXmlNode(MarkupBuilder builder, Map nodeData) {
    // 处理节点的必填/可选字段,避免空指针
    def nodeName = nodeData.name ?: "unknown-node"
    def nodeAttrs = nodeData.attributes ?: [:]
    def childElements = nodeData.elements ?: []
    def nodeText = nodeData.text ?: ""

    // 动态创建节点:用GString指定节点名,传入属性Map
    builder."${nodeName}"(nodeAttrs) {
        // 如果节点有文本内容,先输出文本
        if (nodeText) {
            mkp.yield(nodeText)
        }
        // 递归处理所有子元素
        childElements.each { child ->
            buildXmlNode(builder, child)
        }
    }
}

第三步:生成并输出XML

把递归函数和MarkupBuilder结合起来,生成最终的XML字符串:

def writer = new StringWriter()
def xmlBuilder = new MarkupBuilder(writer)

// 先创建根节点(这里我把根节点名设为"root",你可以根据需求修改)
xmlBuilder.root(xmlData.attributes) {
    // 递归处理根节点下的所有子元素
    xmlData.elements.each { childNode ->
        buildXmlNode(xmlBuilder, childNode)
    }
}

// 打印生成的XML
println writer.toString()

运行结果

上面的代码会生成这样的XML(自动格式化,可读性拉满):

<root xmlVersion='1.0' lang='en'>
  <user id='1001' status='active'>
    <name>John Doe</name>
    <contact type='email'>john@example.com</contact>
    <address zip='90210'>
      <street>123 Main St</street>
      <city>Los Angeles</city>
    </address>
  </user>
  <user id='1002' status='inactive'>
    <name>Jane Smith</name>
  </user>
</root>

关键细节说明

  • 动态节点名:用builder."${nodeName}"这种GString语法,完美支持从Map里读取节点名称,不用写死节点类型
  • 空值容错:用?:运算符处理缺失的attributes、elements字段,避免运行时抛出空指针异常
  • 文本内容支持:额外加了text字段的处理,如果你不需要可以直接删掉这部分逻辑
  • 递归逻辑:在节点的闭包内部递归调用buildXmlNode,自动维护XML的嵌套结构,完全不用手动管理标签的开闭

要是你还有特殊需求(比如自定义XML声明、命名空间、格式化规则),直接在MarkupBuilder初始化的时候加参数就行,比如:

// 开启缩进,指定编码,添加XML声明
def xmlBuilder = new MarkupBuilder(writer)
xmlBuilder.setDoubleQuotes(true) // 用双引号包裹属性值
xmlBuilder.setIndent("  ") // 缩进用两个空格
xmlBuilder.mkp.xmlDeclaration(version: "1.0", encoding: "UTF-8")

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.19 07:23:20