Skip to content

Commit

Permalink
Change Unmarshaler so it uses Node as input.
Browse files Browse the repository at this point in the history
Types implementing the old unmarshaler interface will still work for the
time being for compatibility purposes.
  • Loading branch information
niemeyer committed Mar 14, 2019
1 parent e8e5527 commit e990009
Show file tree
Hide file tree
Showing 3 changed files with 59 additions and 10 deletions.
14 changes: 13 additions & 1 deletion decode.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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
Expand Down
46 changes: 41 additions & 5 deletions decode_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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{}
Expand All @@ -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))
Expand All @@ -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{})
Expand All @@ -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}`
Expand Down
9 changes: 5 additions & 4 deletions yaml.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}

Expand Down

0 comments on commit e990009

Please sign in to comment.