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

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

问题描述

这个问题已经回答了合并顶级地图[字符串]接口{}值对于两个yamls,但是是否可以合并两个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天全站免登陆