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

如何基于Go语言合并多层YAML配置并保留所有嵌套键

递归合并YAML嵌套键的解决方案

需求说明

按顺序加载多个YAML配置文件,合并时保留所有嵌套键:顶层键以后加载的文件值为准,嵌套的Map则递归合并,不丢失原有键值对。

示例文件:
YAML 1:

env: test1
template:
  app:
    database: 
      name: oracle

YAML 2:

env: test2
template:
  app:
    database: 
      version : 12

期望合并结果:

env: test2
template:
  app:
    database: 
      name: oracle
      version: 12

现有问题

使用maps.Copy合并时仅做浅拷贝,遇到同名键会直接替换整个值,导致嵌套层级的原有键丢失,得到错误结果:

env: test2
template:
    app:
        database:
            version: 12

解决方案

实现递归合并函数,遍历每个键值对:

  • 若目标与源的对应值均为map[string]interface{},则递归合并这两个嵌套Map
  • 否则直接用源的值覆盖目标的值

修改configuration结构体的Update方法,替换原有的maps.Copy逻辑:

func mergeMaps(dest, src m) {
    for k, v := range src {
        destVal, exists := dest[k]
        if !exists {
            dest[k] = v
            continue
        }
        // 类型断言判断是否为嵌套Map
        destMap, destIsMap := destVal.(m)
        srcMap, srcIsMap := v.(m)
        if destIsMap && srcIsMap {
            // 递归合并嵌套结构
            mergeMaps(destMap, srcMap)
        } else {
            // 非Map类型直接覆盖
            dest[k] = v
        }
    }
}

func (c *configuration) Update(nc m) {
    if c.c == nil {
        c.c = nc
    } else {
        mergeMaps(c.c, nc)
    }
}

完整代码示例

package main

import (
    "fmt"
    "log"
    "os"
    "path/filepath"

    "gopkg.in/yaml.v3"
)

type configuration struct {
    c  m
    fl []string
}

type m = map[string]interface{}

func mergeMaps(dest, src m) {
    for k, v := range src {
        destVal, exists := dest[k]
        if !exists {
            dest[k] = v
            continue
        }
        destMap, destIsMap := destVal.(m)
        srcMap, srcIsMap := v.(m)
        if destIsMap && srcIsMap {
            mergeMaps(destMap, srcMap)
        } else {
            dest[k] = v
        }
    }
}

func (c *configuration) Update(nc m) {
    if c.c == nil {
        c.c = nc
    } else {
        mergeMaps(c.c, nc)
    }
}

func (c configuration) Print() {
    d, err := yaml.Marshal(&c.c)
    if err != nil {
        log.Fatalf("error: %v", err)
    }
    fmt.Printf("---:\n%s\n\n", string(d))
}

func (c configuration) ParseDir(path string) {

}

func (c *configuration) LoadFromFile(filename string) {
    yf, yfErr := os.ReadFile(filename)
    if yfErr != nil {
        log.Fatal("Error reading the file ", yfErr)
    }
    var data m
    err := yaml.Unmarshal(yf, &data)
    if err != nil {
        log.Fatal(err)
    }
    c.Update(data)
}

func listFiles(path string) []string {
    var returnLf []string
    err := filepath.Walk(path,
        func(path string, info os.FileInfo, err error) error {
            if err != nil {
                return err
            }
            if info.Mode().IsRegular() {
                returnLf = append(returnLf, path)
            }
            return nil
        })
    if err != nil {
        log.Println(err)
    }
    return returnLf
}

// 测试用main函数
func main() {
    cfg := &configuration{}
    cfg.LoadFromFile("yaml1.yaml")
    cfg.LoadFromFile("yaml2.yaml")
    cfg.Print()
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.10 00:46:08