go-yaml / yaml

YAML support for the Go language.
Other
6.87k stars 1.05k forks source link

encoding struct that embeds a non-exported type fails with Go 1.6 #164

Open mwhudson opened 8 years ago

mwhudson commented 8 years ago
package main

import (
    "encoding/json"
    "fmt"

    yaml "gopkg.in/yaml.v2"
)

type bar struct {
    X string
}

type Foo struct {
    bar
}

func main() {
    f := Foo{bar: bar{"x"}}
    j, e := json.Marshal(f)
    if e != nil {
        fmt.Println("json error", e)
    } else {
        fmt.Println("json", string(j))
    }
    s, e := yaml.Marshal(f)
    if e != nil {
        fmt.Println("yaml error", e)
    } else {
        fmt.Println("yaml", string(s))
    }
}

panics with "panic: reflect.Value.Interface: cannot return value obtained from unexported field or method"

This is down to the call of .Interface() on f.bar that marshal() does before seeing if it implements the MarshalYAML method. The rules around whether embedded non-exported types count as exported or not changed in Go 1.6 -- this might count as a bug upstream, or missing API, or something (my head cold is too strong to think through all the issues today).

mwhudson commented 8 years ago

One fix would be to not probe for MarshalYAML on anonymous fields -- it doesn't make a great deal of sense I think?

mjs commented 8 years ago

FWIW, encoding/json will only call MarshalJSON on an anonymous field when there's exactly one anonymous field. MarshalJSON on embedded types is ignored when there's more than one.

I'm not convinced honouring MarshalJSON/MarshalYAML is a good idea for any anonymous field as this means the contents of the containing struct aren't included in the YAML output.