【问题标题】:Read and Merge Two Yaml Files Dynamically and/or Recursively动态和/或递归地读取和合并两个 Yaml 文件
【发布时间】:2021-04-22 09:59:59
【问题描述】:

This question has been answered for merging top level map[string]interface{} values for two yamls,但是可以合并两个yaml文件

A.没有定义结构和

B.有多个未知级别的嵌套?

我尝试解组到相同的空白界面,但覆盖 yaml 完全擦除 基础 yaml。

// using "gopkg.in/yaml.v2"

var i interface{}

d, _ := ioutil.ReadFile("base.yaml")
yaml.Unmarshal(d, &i)

d2, _ = ioutil.ReadFile("override.yaml")
yaml.Unmarshal(d2, &i)

m, _ := yaml.Marshal(i)
ioutil.WriteFile("new.yaml", m, xxxx)

新写入的文件就是override.yaml

如何在不定义结构来封装它们的主体的情况下合并两个具有未知深度的嵌套值的 yaml 文件?我需要执行自定义递归操作还是可以使用已构建的工具来处理?

【问题讨论】:

  • 合并两个yamls文件不会是简单的代码,有一个工具yq可以合并yamls。这也是用 Go 编写的。你可以看看这个。

标签: go yaml


【解决方案1】:

您应该只组合它们,而不是完全解组 YAML 文件。在 YAML 术语中,这意味着构建一个节点图,而不对其进行进一步处理(通常,节点图随后将被处理为本机类型)。 go-yaml 为此提供了 yaml.Node 类型,但您需要不稳定的 v3 版本。

在两个文件都作为节点可用后,您只需对它们执行合并操作,例如

package main

import (
    "errors"
    "gopkg.in/yaml.v3"
    "os"
)

var input1 = []byte(`
a: b
c:
  d: e
`)

var input2 = []byte(`
c:
  f: g
h: i
`)

func nodesEqual(l, r *yaml.Node) bool {
    if l.Kind == yaml.ScalarNode && r.Kind == yaml.ScalarNode {
        return l.Value == r.Value
    }
    panic("equals on non-scalars not implemented!")
}

func recursiveMerge(from, into *yaml.Node) error {
    if from.Kind != into.Kind {
        return errors.New("cannot merge nodes of different kinds")
    }
    switch from.Kind {
    case yaml.MappingNode:
        for i := 0; i < len(from.Content); i += 2 {
            found := false
            for j := 0; j < len(into.Content); j += 2 {
                if nodesEqual(from.Content[i], into.Content[j]) {
                    found = true
                    if err := recursiveMerge(from.Content[i+1], into.Content[j+1]); err != nil {
                        return errors.New("at key "+from.Content[i].Value+": "+err.Error())
                    }
                    break
                }
            }
            if !found {
                into.Content = append(into.Content, from.Content[i:i+2]...)
            }
        }
    case yaml.SequenceNode:
        into.Content = append(into.Content, from.Content...)
    case yaml.DocumentNode:
        recursiveMerge(from.Content[0], into.Content[0])
    default:
        return errors.New("can only merge mapping and sequence nodes")
    }
    return nil
}


func main() {
    var v1, v2 yaml.Node
    yaml.Unmarshal(input1, &v1)
    yaml.Unmarshal(input2, &v2)
    if err := recursiveMerge(&v1, &v2); err != nil {
        panic(err)
    }
    e := yaml.NewEncoder(os.Stdout)
    e.Encode(&v2)
    e.Close()
}

这段代码输出

c:
    f: g
    d: e
h: i
a: b

如您所见,它合并​​了顶层和c: 内部的值。从理论上讲,您还需要在序列和映射节点上实现nodesEqual,因为它们也可以是键,但是这个功能很少使用,所以如果您不需要它可以省略它。此代码还通过简单地将它们连接成一个序列来合并序列,这可能是也可能不是您想要的。如果您还想正确处理别名节点,则需要更多代码。

【讨论】:

    猜你喜欢
    • 2019-01-27
    • 2011-09-17
    • 1970-01-01
    • 2018-05-05
    • 2016-06-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多