动态和/或递归读取和合并两个Yaml文件 [英] Read and Merge Two Yaml Files Dynamically and/or Recursively

查看:89
本文介绍了动态和/或递归读取和合并两个Yaml文件的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

此问题已通过合并顶级map [string] interface {}值得到了解答两个Yaml文件,但是可以合并两个Yaml文件

This question has been answered for merging top level map[string]interface{} values for two yamls, but is it possible to merge two yaml files

A.没有定义结构和

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

B. with multiple unknown levels of nesting?

我尝试将编组到相同的空白界面中,但是覆盖yaml完全消失了基本的Yaml.

I tried unmarshalling into the same blank interface, but the override yaml completely wiped the base 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文件而不定义结构来封装其主体的结构?我需要执行自定义递归操作还是可以使用已经构建的工具来处理?

How can I merge two yaml files that have nested values with a depth unknown without defining structs to encapsulate their bodies? Do I need to to perform a custom recursive operation or can this be handled with tools already built?

推荐答案

不要完全解组YAML文件,而应该组成它们.用YAML术语表示,这意味着无需进一步处理即可构造节点图(通常,该节点图随后将被处理为本机类型).go-yaml为此提供了 yaml.Node 类型,但是您需要不稳定的 v3 版本.

Instead of completely unmarshaling the YAML files, you should only compose them. In YAML terms, this means constructing a node graph, without processing it further (usually, the node graph would then be processed into native types). go-yaml provides the type yaml.Node for that but you'll need the unstable v3 version.

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

After both files are available as nodes, you simply implement the merging operation on them, e.g.

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 ,因为它们也可以是键,但是很少使用此功能,因此在不需要时可以将其忽略.该代码还通过简单地将序列连接为单个序列来合并序列,这可能是您想要的,也可能不是您想要的.如果您还想正确处理别名节点,则需要更多代码.

As you can see, it merges values both at the top level and inside c:. Theoretically, you need to implement nodesEqual also on sequence and mapping nodes because those can also be keys, but this feature is rarely used so you can leave it out if you don't need it. This code also merges sequences by simply concatenating them into a single sequence, this may or may not be what you want. More code is required if you want to also properly process alias nodes.

这篇关于动态和/或递归读取和合并两个Yaml文件的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆