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

Bring back old unmarshaler type error behavior.

Closes #499.
Gustavo Niemeyer 6 лет назад
Родитель
Сommit
35294daf73
2 измененных файлов с 126 добавлено и 10 удалено
  1. 6 1
      decode.go
  2. 120 9
      decode_test.go

+ 6 - 1
decode.go

@@ -350,7 +350,12 @@ func (d *decoder) terror(n *Node, tag string, out reflect.Value) {
 }
 
 func (d *decoder) callUnmarshaler(n *Node, u Unmarshaler) (good bool) {
-	if err := u.UnmarshalYAML(n); err != nil {
+	err := u.UnmarshalYAML(n)
+	if e, ok := err.(*TypeError); ok {
+		d.terrors = append(d.terrors, e.Errors...)
+		return false
+	}
+	if err != nil {
 		fail(err)
 	}
 	return true

+ 120 - 9
decode_test.go

@@ -1115,6 +1115,37 @@ func (s *S) TestUnmarshalerTypeError(c *C) {
 		delete(unmarshalerResult, 4)
 	}()
 
+	type T struct {
+		Before int
+		After  int
+		M      map[string]*unmarshalerType
+	}
+	var v T
+	data := `{before: A, m: {abc: 1, def: 2, ghi: 3, jkl: 4}, after: B}`
+	err := yaml.Unmarshal([]byte(data), &v)
+	c.Assert(err, ErrorMatches, ""+
+		"yaml: unmarshal errors:\n"+
+		"  line 1: cannot unmarshal !!str `A` into int\n"+
+		"  foo\n"+
+		"  bar\n"+
+		"  line 1: cannot unmarshal !!str `B` into int")
+	c.Assert(v.M["abc"], NotNil)
+	c.Assert(v.M["def"], IsNil)
+	c.Assert(v.M["ghi"], NotNil)
+	c.Assert(v.M["jkl"], IsNil)
+
+	c.Assert(v.M["abc"].value, Equals, 1)
+	c.Assert(v.M["ghi"].value, Equals, 3)
+}
+
+func (s *S) TestObsoleteUnmarshalerTypeError(c *C) {
+	unmarshalerResult[2] = &yaml.TypeError{[]string{"foo"}}
+	unmarshalerResult[4] = &yaml.TypeError{[]string{"bar"}}
+	defer func() {
+		delete(unmarshalerResult, 2)
+		delete(unmarshalerResult, 4)
+	}()
+
 	type T struct {
 		Before int
 		After  int
@@ -1140,7 +1171,45 @@ func (s *S) TestUnmarshalerTypeError(c *C) {
 
 type proxyTypeError struct{}
 
-func (v *proxyTypeError) UnmarshalYAML(unmarshal func(interface{}) error) error {
+func (v *proxyTypeError) UnmarshalYAML(node *yaml.Node) error {
+	var s string
+	var a int32
+	var b int64
+	if err := node.Decode(&s); err != nil {
+		panic(err)
+	}
+	if s == "a" {
+		if err := node.Decode(&b); err == nil {
+			panic("should have failed")
+		}
+		return node.Decode(&a)
+	}
+	if err := node.Decode(&a); err == nil {
+		panic("should have failed")
+	}
+	return node.Decode(&b)
+}
+
+func (s *S) TestUnmarshalerTypeErrorProxying(c *C) {
+	type T struct {
+		Before int
+		After  int
+		M      map[string]*proxyTypeError
+	}
+	var v T
+	data := `{before: A, m: {abc: a, def: b}, after: B}`
+	err := yaml.Unmarshal([]byte(data), &v)
+	c.Assert(err, ErrorMatches, ""+
+		"yaml: unmarshal errors:\n"+
+		"  line 1: cannot unmarshal !!str `A` into int\n"+
+		"  line 1: cannot unmarshal !!str `a` into int32\n"+
+		"  line 1: cannot unmarshal !!str `b` into int64\n"+
+		"  line 1: cannot unmarshal !!str `B` into int")
+}
+
+type obsoleteProxyTypeError struct{}
+
+func (v *obsoleteProxyTypeError) UnmarshalYAML(unmarshal func(interface{}) error) error {
 	var s string
 	var a int32
 	var b int64
@@ -1159,11 +1228,11 @@ func (v *proxyTypeError) UnmarshalYAML(unmarshal func(interface{}) error) error
 	return unmarshal(&b)
 }
 
-func (s *S) TestUnmarshalerTypeErrorProxying(c *C) {
+func (s *S) TestObsoleteUnmarshalerTypeErrorProxying(c *C) {
 	type T struct {
 		Before int
 		After  int
-		M      map[string]*proxyTypeError
+		M      map[string]*obsoleteProxyTypeError
 	}
 	var v T
 	data := `{before: A, m: {abc: a, def: b}, after: B}`
@@ -1176,11 +1245,11 @@ func (s *S) TestUnmarshalerTypeErrorProxying(c *C) {
 		"  line 1: cannot unmarshal !!str `B` into int")
 }
 
-type failingUnmarshaler struct{}
-
 var failingErr = errors.New("failingErr")
 
-func (ft *failingUnmarshaler) UnmarshalYAML(unmarshal func(interface{}) error) error {
+type failingUnmarshaler struct{}
+
+func (ft *failingUnmarshaler) UnmarshalYAML(node *yaml.Node) error {
 	return failingErr
 }
 
@@ -1189,18 +1258,29 @@ func (s *S) TestUnmarshalerError(c *C) {
 	c.Assert(err, Equals, failingErr)
 }
 
+type obsoleteFailingUnmarshaler struct{}
+
+func (ft *obsoleteFailingUnmarshaler) UnmarshalYAML(unmarshal func(interface{}) error) error {
+	return failingErr
+}
+
+func (s *S) TestObsoleteUnmarshalerError(c *C) {
+	err := yaml.Unmarshal([]byte("a: b"), &obsoleteFailingUnmarshaler{})
+	c.Assert(err, Equals, failingErr)
+}
+
 type sliceUnmarshaler []int
 
-func (su *sliceUnmarshaler) UnmarshalYAML(unmarshal func(interface{}) error) error {
+func (su *sliceUnmarshaler) UnmarshalYAML(node *yaml.Node) error {
 	var slice []int
-	err := unmarshal(&slice)
+	err := node.Decode(&slice)
 	if err == nil {
 		*su = slice
 		return nil
 	}
 
 	var intVal int
-	err = unmarshal(&intVal)
+	err = node.Decode(&intVal)
 	if err == nil {
 		*su = []int{intVal}
 		return nil
@@ -1220,6 +1300,37 @@ func (s *S) TestUnmarshalerRetry(c *C) {
 	c.Assert(su, DeepEquals, sliceUnmarshaler([]int{1}))
 }
 
+type obsoleteSliceUnmarshaler []int
+
+func (su *obsoleteSliceUnmarshaler) UnmarshalYAML(unmarshal func(interface{}) error) error {
+	var slice []int
+	err := unmarshal(&slice)
+	if err == nil {
+		*su = slice
+		return nil
+	}
+
+	var intVal int
+	err = unmarshal(&intVal)
+	if err == nil {
+		*su = []int{intVal}
+		return nil
+	}
+
+	return err
+}
+
+func (s *S) TestObsoleteUnmarshalerRetry(c *C) {
+	var su obsoleteSliceUnmarshaler
+	err := yaml.Unmarshal([]byte("[1, 2, 3]"), &su)
+	c.Assert(err, IsNil)
+	c.Assert(su, DeepEquals, obsoleteSliceUnmarshaler([]int{1, 2, 3}))
+
+	err = yaml.Unmarshal([]byte("1"), &su)
+	c.Assert(err, IsNil)
+	c.Assert(su, DeepEquals, obsoleteSliceUnmarshaler([]int{1}))
+}
+
 // From http://yaml.org/type/merge.html
 var mergeTests = `
 anchors: