Skip to content

Commit

Permalink
Enforce unique mapping keys and drop the option.
Browse files Browse the repository at this point in the history
  • Loading branch information
niemeyer committed Mar 21, 2019
1 parent 085f382 commit 309c9d1
Show file tree
Hide file tree
Showing 3 changed files with 34 additions and 34 deletions.
31 changes: 19 additions & 12 deletions decode.go
Original file line number Diff line number Diff line change
Expand Up @@ -348,7 +348,7 @@ var (
)

func newDecoder() *decoder {
d := &decoder{mapType: defaultMapType}
d := &decoder{mapType: defaultMapType, uniqueKeys: true}
d.aliases = make(map[*Node]bool)
return d
}
Expand Down Expand Up @@ -698,6 +698,22 @@ func (d *decoder) sequence(n *Node, out reflect.Value) (good bool) {
}

func (d *decoder) mapping(n *Node, out reflect.Value) (good bool) {
l := len(n.Children)
if d.uniqueKeys {
nerrs := len(d.terrors)
for i := 0; i < l; i += 2 {
ni := n.Children[i]
for j := i+2; j < l; j += 2 {
nj := n.Children[j]
if ni.Kind == nj.Kind && ni.Value == nj.Value {
d.terrors = append(d.terrors, fmt.Sprintf("line %d: mapping key %#v already defined at line %d", nj.Line, nj.Value, ni.Line))
}
}
}
if len(d.terrors) > nerrs {
return false
}
}
switch out.Kind() {
case reflect.Struct:
return d.mappingStruct(n, out)
Expand All @@ -723,7 +739,6 @@ func (d *decoder) mapping(n *Node, out reflect.Value) (good bool) {
if out.IsNil() {
out.Set(reflect.MakeMap(outt))
}
l := len(n.Children)
for i := 0; i < l; i += 2 {
if isMerge(n.Children[i]) {
d.merge(n.Children[i+1], out)
Expand All @@ -740,22 +755,14 @@ func (d *decoder) mapping(n *Node, out reflect.Value) (good bool) {
}
e := reflect.New(et).Elem()
if d.unmarshal(n.Children[i+1], e) {
d.setMapIndex(n.Children[i+1], out, k, e)
out.SetMapIndex(k, e)
}
}
}
d.mapType = mapType
return true
}

func (d *decoder) setMapIndex(n *Node, out, k, v reflect.Value) {
if d.uniqueKeys && out.MapIndex(k) != zeroValue {
d.terrors = append(d.terrors, fmt.Sprintf("line %d: key %#v already set in map", n.Line, k.Interface()))
return
}
out.SetMapIndex(k, v)
}

func (d *decoder) mappingStruct(n *Node, out reflect.Value) (good bool) {
sinfo, err := getStructInfo(out.Type())
if err != nil {
Expand Down Expand Up @@ -806,7 +813,7 @@ func (d *decoder) mappingStruct(n *Node, out reflect.Value) (good bool) {
}
value := reflect.New(elemType).Elem()
d.unmarshal(n.Children[i+1], value)
d.setMapIndex(n.Children[i+1], inlineMap, name, value)
inlineMap.SetMapIndex(name, value)
} else if d.knownFields {
d.terrors = append(d.terrors, fmt.Sprintf("line %d: field %s not found in type %s", ni.Line, name.String(), out.Type()))
}
Expand Down
29 changes: 15 additions & 14 deletions decode_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1211,7 +1211,7 @@ var unmarshalStrictTests = []struct {
unique: true,
data: "a: 1\nb: 2\na: 3\n",
value: struct{ A, B int }{A: 3, B: 2},
error: `yaml: unmarshal errors:\n line 3: field a already set in type struct { A int; B int }`,
error: `yaml: unmarshal errors:\n line 3: mapping key "a" already defined at line 1`,
}, {
unique: true,
data: "c: 3\na: 1\nb: 2\nc: 4\n",
Expand All @@ -1227,7 +1227,7 @@ var unmarshalStrictTests = []struct {
},
},
},
error: `yaml: unmarshal errors:\n line 4: field c already set in type struct { A int; yaml_test.inlineB "yaml:\\",inline\\"" }`,
error: `yaml: unmarshal errors:\n line 4: mapping key "c" already defined at line 1`,
}, {
unique: true,
data: "c: 0\na: 1\nb: 2\nc: 1\n",
Expand All @@ -1243,7 +1243,7 @@ var unmarshalStrictTests = []struct {
},
},
},
error: `yaml: unmarshal errors:\n line 4: field c already set in type struct { A int; yaml_test.inlineB "yaml:\\",inline\\"" }`,
error: `yaml: unmarshal errors:\n line 4: mapping key "c" already defined at line 1`,
}, {
unique: true,
data: "c: 1\na: 1\nb: 2\nc: 3\n",
Expand All @@ -1257,7 +1257,7 @@ var unmarshalStrictTests = []struct {
"c": 3,
},
},
error: `yaml: unmarshal errors:\n line 4: key "c" already set in map`,
error: `yaml: unmarshal errors:\n line 4: mapping key "c" already defined at line 1`,
}, {
unique: true,
data: "a: 1\n9: 2\nnull: 3\n9: 4",
Expand All @@ -1266,26 +1266,27 @@ var unmarshalStrictTests = []struct {
nil: 3,
9: 4,
},
error: `yaml: unmarshal errors:\n line 4: key 9 already set in map`,
error: `yaml: unmarshal errors:\n line 4: mapping key "9" already defined at line 2`,
}}

func (s *S) TestUnmarshalKnownFields(c *C) {
for i, item := range unmarshalStrictTests {
c.Logf("test %d: %q", i, item.data)
// First test that normal Unmarshal unmarshals to the expected value.
t := reflect.ValueOf(item.value).Type()
value := reflect.New(t)
err := yaml.Unmarshal([]byte(item.data), value.Interface())
c.Assert(err, Equals, nil)
c.Assert(value.Elem().Interface(), DeepEquals, item.value)
if !item.unique {
t := reflect.ValueOf(item.value).Type()
value := reflect.New(t)
err := yaml.Unmarshal([]byte(item.data), value.Interface())
c.Assert(err, Equals, nil)
c.Assert(value.Elem().Interface(), DeepEquals, item.value)
}

// Then test that it fails on the same thing with KnownFields on.
t = reflect.ValueOf(item.value).Type()
value = reflect.New(t)
t := reflect.ValueOf(item.value).Type()
value := reflect.New(t)
dec := yaml.NewDecoder(bytes.NewBuffer([]byte(item.data)))
dec.KnownFields(item.known)
dec.UniqueKeys(item.unique)
err = dec.Decode(value.Interface())
err := dec.Decode(value.Interface())
c.Assert(err, ErrorMatches, item.error)
}
}
Expand Down
8 changes: 0 additions & 8 deletions yaml.go
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,6 @@ func Unmarshal(in []byte, out interface{}) (err error) {
type Decoder struct {
parser *parser
knownFields bool
uniqueKeys bool
}

// NewDecoder returns a new decoder that reads from r.
Expand All @@ -96,12 +95,6 @@ func (dec *Decoder) KnownFields(enable bool) {
dec.knownFields = enable
}

// UniqueKeys enforces the keys in decoded mappings to exist
// only once inside the given mapping.
func (dec *Decoder) UniqueKeys(enable bool) {
dec.uniqueKeys = enable
}

// Decode reads the next YAML-encoded value from its input
// and stores it in the value pointed to by v.
//
Expand All @@ -110,7 +103,6 @@ func (dec *Decoder) UniqueKeys(enable bool) {
func (dec *Decoder) Decode(v interface{}) (err error) {
d := newDecoder()
d.knownFields = dec.knownFields
d.uniqueKeys = dec.uniqueKeys
defer handleErr(&err)
node := dec.parser.parse()
if node == nil {
Expand Down

0 comments on commit 309c9d1

Please sign in to comment.