MarshalText interface is ignored on nested structs
TheFriendlyCoder opened this issue · comments
Given the following example structs:
type MyChild struct {
nested string
}
func (c *MyChild) MarshalText() ([]byte, error) {
return []byte(c.nested), nil
}
type MyParent struct {
Child MyChild
}
If you attempt to marshal an instance of MyParent to YAML by doing something like this:
c := MyChild{
nested: "Hello",
}
p := MyParent{
Child: c,
}
data, _ := yaml.Marshal(&p)
fmt.Printf("%+v\n", string(data))
You get the following output:
child: {}
Instead of the expected output of:
child:
nested: Hello
If, however, you attempt to marshal and instance of the MyChild struct directly, as in:
data, _ := yaml.Marshal(&c)
fmt.Printf("%+v\n", string(data))
You get the expected output:
Hello
NOTE: It appears as though the MarshalYAML() interface is also ignored on child / nested structs as well, as in the following example:
func (c *MyChild) MarshalYAML() (interface{}, error) {
return []byte(c.nested), nil
}
I did a bit more ad-hoc testing on this issue, and the problem seems to be related to whether the marshaller method takes a pointer or reference receiver. Using the sample code from above, if I have a struct that contains a reference to a value object, then the marshaller method needs to have a reference receiver:
type MyChild struct {
nested string
}
func (c MyChild) MarshalText() ([]byte, error) { // <----- notice the reference receiver "c MyChild"
return []byte(c.nested), nil
}
type MyParent struct {
Child MyChild // <----- notice the reference value here
}
Similarly, if I have a struct that contains a pointer to a value instead of a reference, the marshaller interface must use a pointer-receiver as in:
type MyChild struct {
nested string
}
func (c *MyChild) MarshalText() ([]byte, error) { // <----- notice the pointer receiver here "c *MyChild"
return []byte(c.nested), nil
}
type MyParent struct {
Child *MyChild // <------- notice the pointer to the value here
}
The issue then becomes, how can one implement a marshal implementation for a structure so as to allow both reference and pointer values to be serialized properly?
I did a bit more ad-hoc testing on this issue, and the problem seems to be related to whether the marshaller method takes a pointer or reference receiver. Using the sample code from above, if I have a struct that contains a reference to a value object, then the marshaller method needs to have a reference receiver:
type MyChild struct { nested string } func (c MyChild) MarshalText() ([]byte, error) { // <----- notice the reference receiver "c MyChild" return []byte(c.nested), nil } type MyParent struct { Child MyChild // <----- notice the reference value here }
Similarly, if I have a struct that contains a pointer to a value instead of a reference, the marshaller interface must use a pointer-receiver as in:
type MyChild struct { nested string } func (c *MyChild) MarshalText() ([]byte, error) { // <----- notice the pointer receiver here "c *MyChild" return []byte(c.nested), nil } type MyParent struct { Child *MyChild // <------- notice the pointer to the value here }
The issue then becomes, how can one implement a marshal implementation for a structure so as to allow both reference and pointer values to be serialized properly?
I can confirm this is the case
See #979