如何基于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
相关产品推荐
相关产品推荐

