您应该只组合它们,而不是完全解组 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,因为它们也可以是键,但是这个功能很少使用,所以如果您不需要它可以省略它。此代码还通过简单地将它们连接成一个序列来合并序列,这可能是也可能不是您想要的。如果您还想正确处理别名节点,则需要更多代码。