Browse Source

Change Unmarshaler so it uses Node as input.

Types implementing the old unmarshaler interface will still work for the
time being for compatibility purposes.
Gustavo Niemeyer 7 years ago
parent
commit
e990009760
3 changed files with 59 additions and 10 deletions
  1. 13 1
      decode.go
  2. 41 5
      decode_test.go
  3. 5 4
      yaml.go

+ 13 - 1
decode.go

@@ -367,6 +367,13 @@ 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 {
+		fail(err)
+	}
+	return true
+}
+
+func (d *decoder) callObsoleteUnmarshaler(n *Node, u obsoleteUnmarshaler) (good bool) {
 	terrlen := len(d.terrors)
 	err := u.UnmarshalYAML(func(v interface{}) (err error) {
 		defer handleErr(&err)
@@ -410,10 +417,15 @@ func (d *decoder) prepare(n *Node, out reflect.Value) (newout reflect.Value, unm
 			again = true
 		}
 		if out.CanAddr() {
-			if u, ok := out.Addr().Interface().(Unmarshaler); ok {
+			outi := out.Addr().Interface()
+			if u, ok := outi.(Unmarshaler); ok {
 				good = d.callUnmarshaler(n, u)
 				return out, true, good
 			}
+			if u, ok := outi.(obsoleteUnmarshaler); ok {
+				good = d.callObsoleteUnmarshaler(n, u)
+				return out, true, good
+			}
 		}
 	}
 	return out, false, false

+ 41 - 5
decode_test.go

@@ -863,8 +863,8 @@ type unmarshalerType struct {
 	value interface{}
 }
 
-func (o *unmarshalerType) UnmarshalYAML(unmarshal func(v interface{}) error) error {
-	if err := unmarshal(&o.value); err != nil {
+func (o *unmarshalerType) UnmarshalYAML(value *yaml.Node) error {
+	if err := value.Decode(&o.value); err != nil {
 		return err
 	}
 	if i, ok := o.value.(int); ok {
@@ -883,6 +883,31 @@ type unmarshalerValue struct {
 	Field unmarshalerType "_"
 }
 
+
+type obsoleteUnmarshalerType struct {
+	value interface{}
+}
+
+func (o *obsoleteUnmarshalerType) UnmarshalYAML(unmarshal func(v interface{}) error) error {
+	if err := unmarshal(&o.value); err != nil {
+		return err
+	}
+	if i, ok := o.value.(int); ok {
+		if result, ok := unmarshalerResult[i]; ok {
+			return result
+		}
+	}
+	return nil
+}
+
+type obsoleteUnmarshalerPointer struct {
+	Field *obsoleteUnmarshalerType "_"
+}
+
+type obsoleteUnmarshalerValue struct {
+	Field obsoleteUnmarshalerType "_"
+}
+
 func (s *S) TestUnmarshalerPointerField(c *C) {
 	for _, item := range unmarshalerTests {
 		obj := &unmarshalerPointer{}
@@ -895,11 +920,22 @@ func (s *S) TestUnmarshalerPointerField(c *C) {
 			c.Assert(obj.Field.value, DeepEquals, item.value)
 		}
 	}
+	for _, item := range unmarshalerTests {
+		obj := &obsoleteUnmarshalerPointer{}
+		err := yaml.Unmarshal([]byte(item.data), obj)
+		c.Assert(err, IsNil)
+		if item.value == nil {
+			c.Assert(obj.Field, IsNil)
+		} else {
+			c.Assert(obj.Field, NotNil, Commentf("Pointer not initialized (%#v)", item.value))
+			c.Assert(obj.Field.value, DeepEquals, item.value)
+		}
+	}
 }
 
 func (s *S) TestUnmarshalerValueField(c *C) {
 	for _, item := range unmarshalerTests {
-		obj := &unmarshalerValue{}
+		obj := &obsoleteUnmarshalerValue{}
 		err := yaml.Unmarshal([]byte(item.data), obj)
 		c.Assert(err, IsNil)
 		c.Assert(obj.Field, NotNil, Commentf("Pointer not initialized (%#v)", item.value))
@@ -908,7 +944,7 @@ func (s *S) TestUnmarshalerValueField(c *C) {
 }
 
 func (s *S) TestUnmarshalerWholeDocument(c *C) {
-	obj := &unmarshalerType{}
+	obj := &obsoleteUnmarshalerType{}
 	err := yaml.Unmarshal([]byte(unmarshalerTests[0].data), obj)
 	c.Assert(err, IsNil)
 	value, ok := obj.value.(map[interface{}]interface{})
@@ -927,7 +963,7 @@ func (s *S) TestUnmarshalerTypeError(c *C) {
 	type T struct {
 		Before int
 		After  int
-		M      map[string]*unmarshalerType
+		M      map[string]*obsoleteUnmarshalerType
 	}
 	var v T
 	data := `{before: A, m: {abc: 1, def: 2, ghi: 3, jkl: 4}, after: B}`

+ 5 - 4
yaml.go

@@ -16,11 +16,12 @@ import (
 )
 
 // The Unmarshaler interface may be implemented by types to customize their
-// behavior when being unmarshaled from a YAML document. The UnmarshalYAML
-// method receives a function that may be called to unmarshal the original
-// YAML value into a field or variable. It is safe to call the unmarshal
-// function parameter more than once if necessary.
+// behavior when being unmarshaled from a YAML document.
 type Unmarshaler interface {
+	UnmarshalYAML(value *Node) error
+}
+
+type obsoleteUnmarshaler interface {
 	UnmarshalYAML(unmarshal func(interface{}) error) error
 }