Skip to content

Commit

Permalink
Allow multiple merge keys
Browse files Browse the repository at this point in the history
  • Loading branch information
moskyb committed Jul 21, 2023
1 parent 4b21293 commit 099718d
Show file tree
Hide file tree
Showing 2 changed files with 31 additions and 5 deletions.
11 changes: 6 additions & 5 deletions decode.go
Original file line number Diff line number Diff line change
Expand Up @@ -772,7 +772,8 @@ func (d *decoder) mapping(n *Node, out reflect.Value) (good bool) {
ni := n.Content[i]
for j := i + 2; j < l; j += 2 {
nj := n.Content[j]
if ni.Kind == nj.Kind && ni.Value == nj.Value {
// Key collisions are allowed if both of the keys are merges - each merge will be applied in order, leading to no collisions in the final decoded document
if ni.Kind == nj.Kind && ni.Value == nj.Value && !isMerge(nj) {
d.terrors = append(d.terrors, fmt.Sprintf("line %d: mapping key %#v already defined at line %d", nj.Line, nj.Value, ni.Line))
}
}
Expand Down Expand Up @@ -816,7 +817,7 @@ func (d *decoder) mapping(n *Node, out reflect.Value) (good bool) {
mergedFields := d.mergedFields
d.mergedFields = nil

var mergeNode *Node
mergeNodes := []*Node{}

mapIsNew := false
if out.IsNil() {
Expand All @@ -825,7 +826,7 @@ func (d *decoder) mapping(n *Node, out reflect.Value) (good bool) {
}
for i := 0; i < l; i += 2 {
if isMerge(n.Content[i]) {
mergeNode = n.Content[i+1]
mergeNodes = append(mergeNodes, n.Content[i+1])
continue
}
k := reflect.New(kt).Elem()
Expand All @@ -852,8 +853,8 @@ func (d *decoder) mapping(n *Node, out reflect.Value) (good bool) {
}

d.mergedFields = mergedFields
if mergeNode != nil {
d.merge(n, mergeNode, out)
for _, node := range mergeNodes {
d.merge(n, node, out)
}

d.stringMapType = stringMapType
Expand Down
25 changes: 25 additions & 0 deletions decode_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1530,6 +1530,31 @@ func (s *S) TestMergeNestedStruct(c *C) {
c.Assert(testm["outer"], DeepEquals, wantm)
}

const doubleMerge = `
one: &one
a: 1
b: 2
two: &two
b: 3
c: 4
merged:
<<: *one
<<: *two
`

func (s *S) TestDoubleMerge(c *C) {
var m map[string]interface{}
err := yaml.Unmarshal([]byte(doubleMerge), &m)
c.Assert(err, IsNil)
c.Assert(m["merged"], DeepEquals, map[string]interface{}{
"a": 1,
"b": 3,
"c": 4,
})
}

var unmarshalNullTests = []struct {
input string
pristine, expected func() interface{}
Expand Down

0 comments on commit 099718d

Please sign in to comment.