Просмотр исходного кода

Split Strict flag into KnownFields and UniqueKeys.

Gustavo Niemeyer 7 лет назад
Родитель
Сommit
085f3822cc
3 измененных файлов с 52 добавлено и 38 удалено
  1. 9 7
      decode.go
  2. 25 14
      decode_test.go
  3. 18 17
      yaml.go

+ 9 - 7
decode.go

@@ -333,7 +333,9 @@ type decoder struct {
 	aliases map[*Node]bool
 	mapType reflect.Type
 	terrors []string
-	strict  bool
+
+	knownFields bool
+	uniqueKeys  bool
 }
 
 var (
@@ -345,8 +347,8 @@ var (
 	ptrTimeType    = reflect.TypeOf(&time.Time{})
 )
 
-func newDecoder(strict bool) *decoder {
-	d := &decoder{mapType: defaultMapType, strict: strict}
+func newDecoder() *decoder {
+	d := &decoder{mapType: defaultMapType}
 	d.aliases = make(map[*Node]bool)
 	return d
 }
@@ -747,7 +749,7 @@ func (d *decoder) mapping(n *Node, out reflect.Value) (good bool) {
 }
 
 func (d *decoder) setMapIndex(n *Node, out, k, v reflect.Value) {
-	if d.strict && out.MapIndex(k) != zeroValue {
+	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
 	}
@@ -771,7 +773,7 @@ func (d *decoder) mappingStruct(n *Node, out reflect.Value) (good bool) {
 	}
 
 	var doneFields []bool
-	if d.strict {
+	if d.uniqueKeys {
 		doneFields = make([]bool, len(sinfo.FieldsList))
 	}
 	for i := 0; i < l; i += 2 {
@@ -784,7 +786,7 @@ func (d *decoder) mappingStruct(n *Node, out reflect.Value) (good bool) {
 			continue
 		}
 		if info, ok := sinfo.FieldsMap[name.String()]; ok {
-			if d.strict {
+			if d.uniqueKeys {
 				if doneFields[info.Id] {
 					d.terrors = append(d.terrors, fmt.Sprintf("line %d: field %s already set in type %s", ni.Line, name.String(), out.Type()))
 					continue
@@ -805,7 +807,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)
-		} else if d.strict {
+		} 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()))
 		}
 	}

+ 25 - 14
decode_test.go

@@ -1,6 +1,7 @@
 package yaml_test
 
 import (
+	"bytes"
 	"errors"
 	"io"
 	"math"
@@ -890,7 +891,6 @@ type unmarshalerValue struct {
 	Field unmarshalerType "_"
 }
 
-
 type obsoleteUnmarshalerType struct {
 	value interface{}
 }
@@ -1197,19 +1197,24 @@ func (s *S) TestUnmarshalSliceOnPreset(c *C) {
 }
 
 var unmarshalStrictTests = []struct {
-	data  string
-	value interface{}
-	error string
+	known  bool
+	unique bool
+	data   string
+	value  interface{}
+	error  string
 }{{
+	known: true,
 	data:  "a: 1\nc: 2\n",
 	value: struct{ A, B int }{A: 1},
 	error: `yaml: unmarshal errors:\n  line 2: field c not found in type struct { A int; B int }`,
 }, {
-	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 }`,
+	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 }`,
 }, {
-	data: "c: 3\na: 1\nb: 2\nc: 4\n",
+	unique: true,
+	data:   "c: 3\na: 1\nb: 2\nc: 4\n",
 	value: struct {
 		A       int
 		inlineB `yaml:",inline"`
@@ -1224,7 +1229,8 @@ var unmarshalStrictTests = []struct {
 	},
 	error: `yaml: unmarshal errors:\n  line 4: field c already set in type struct { A int; yaml_test.inlineB "yaml:\\",inline\\"" }`,
 }, {
-	data: "c: 0\na: 1\nb: 2\nc: 1\n",
+	unique: true,
+	data:   "c: 0\na: 1\nb: 2\nc: 1\n",
 	value: struct {
 		A       int
 		inlineB `yaml:",inline"`
@@ -1239,7 +1245,8 @@ var unmarshalStrictTests = []struct {
 	},
 	error: `yaml: unmarshal errors:\n  line 4: field c already set in type struct { A int; yaml_test.inlineB "yaml:\\",inline\\"" }`,
 }, {
-	data: "c: 1\na: 1\nb: 2\nc: 3\n",
+	unique: true,
+	data:   "c: 1\na: 1\nb: 2\nc: 3\n",
 	value: struct {
 		A int
 		M map[string]interface{} `yaml:",inline"`
@@ -1252,7 +1259,8 @@ var unmarshalStrictTests = []struct {
 	},
 	error: `yaml: unmarshal errors:\n  line 4: key "c" already set in map`,
 }, {
-	data: "a: 1\n9: 2\nnull: 3\n9: 4",
+	unique: true,
+	data:   "a: 1\n9: 2\nnull: 3\n9: 4",
 	value: map[interface{}]interface{}{
 		"a": 1,
 		nil: 3,
@@ -1261,7 +1269,7 @@ var unmarshalStrictTests = []struct {
 	error: `yaml: unmarshal errors:\n  line 4: key 9 already set in map`,
 }}
 
-func (s *S) TestUnmarshalStrict(c *C) {
+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.
@@ -1271,10 +1279,13 @@ func (s *S) TestUnmarshalStrict(c *C) {
 		c.Assert(err, Equals, nil)
 		c.Assert(value.Elem().Interface(), DeepEquals, item.value)
 
-		// Then test that UnmarshalStrict fails on the same thing.
+		// Then test that it fails on the same thing with KnownFields on.
 		t = reflect.ValueOf(item.value).Type()
 		value = reflect.New(t)
-		err = yaml.UnmarshalStrict([]byte(item.data), value.Interface())
+		dec := yaml.NewDecoder(bytes.NewBuffer([]byte(item.data)))
+		dec.KnownFields(item.known)
+		dec.UniqueKeys(item.unique)
+		err = dec.Decode(value.Interface())
 		c.Assert(err, ErrorMatches, item.error)
 	}
 }

+ 18 - 17
yaml.go

@@ -73,18 +73,11 @@ func Unmarshal(in []byte, out interface{}) (err error) {
 	return unmarshal(in, out, false)
 }
 
-// UnmarshalStrict is like Unmarshal except that any fields that are found
-// in the data that do not have corresponding struct members, or mapping
-// keys that are duplicates, will result in
-// an error.
-func UnmarshalStrict(in []byte, out interface{}) (err error) {
-	return unmarshal(in, out, true)
-}
-
 // A Decorder reads and decodes YAML values from an input stream.
 type Decoder struct {
-	strict bool
-	parser *parser
+	parser      *parser
+	knownFields bool
+	uniqueKeys  bool
 }
 
 // NewDecoder returns a new decoder that reads from r.
@@ -97,10 +90,16 @@ func NewDecoder(r io.Reader) *Decoder {
 	}
 }
 
-// SetStrict sets whether strict decoding behaviour is enabled when
-// decoding items in the data (see UnmarshalStrict). By default, decoding is not strict.
-func (dec *Decoder) SetStrict(strict bool) {
-	dec.strict = strict
+// KnownFields ensures that the keys in decoded mappings to
+// exist as fields in the struct being decoded into.
+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
@@ -109,7 +108,9 @@ func (dec *Decoder) SetStrict(strict bool) {
 // See the documentation for Unmarshal for details about the
 // conversion of YAML into a Go value.
 func (dec *Decoder) Decode(v interface{}) (err error) {
-	d := newDecoder(dec.strict)
+	d := newDecoder()
+	d.knownFields = dec.knownFields
+	d.uniqueKeys = dec.uniqueKeys
 	defer handleErr(&err)
 	node := dec.parser.parse()
 	if node == nil {
@@ -127,7 +128,7 @@ func (dec *Decoder) Decode(v interface{}) (err error) {
 }
 
 func (n *Node) Decode(v interface{}) (err error) {
-	d := newDecoder(false)
+	d := newDecoder()
 	defer handleErr(&err)
 	out := reflect.ValueOf(v)
 	if out.Kind() == reflect.Ptr && !out.IsNil() {
@@ -142,7 +143,7 @@ func (n *Node) Decode(v interface{}) (err error) {
 
 func unmarshal(in []byte, out interface{}, strict bool) (err error) {
 	defer handleErr(&err)
-	d := newDecoder(strict)
+	d := newDecoder()
 	p := newParser(in)
 	defer p.destroy()
 	node := p.parse()