go: encoding/json: cannot unmarshal into unexported embedded pointer types

A consequence of fixing #21353 is that you cannot round-trip marshal/unmarshal the following:

type embed struct{ X int }
type S struct{ *embed }

in := &S{&embed{5}}
out := &S{}

b, _ := json.Marshal(in) // string(b) == `{"X": 5}`

// Panics, since out.embed is nil and it cannot initialize that field.
json.Unmarshal(b, out)

// Manually allocated embed would allow this to work:
out.embed = &embed{}
json.Unmarshal(b, out)

The problem is that the json package had been relying on a bug within the reflect package, where it was able to allocate the unexported field. With that being fixed in #21353, what should be the correct behavior when unmarshaling into a pointer of an embedded unexported struct type? We can either:

  • Always ignore the embedded struct
  • Only ignore the embedded struct if not allocated
  • Return an error when the embedded struct is not allocated

I believe the 3rd option is the least surprising.

\cc @zombiezen @adams-sarah @cybrcodr @jba @pongad

About this issue

  • Original URL
  • State: closed
  • Created 7 years ago
  • Comments: 31 (27 by maintainers)

Commits related to this issue

Most upvoted comments

I looked through this again. This is really unfortunate. Going back to Joe’s original example:

type embed struct{ X int }
type S struct{ *embed }

in := &S{&embed{5}}
out := &S{}

b, _ := json.Marshal(in) // string(b) == `{"X": 5}`

// Panics, since out.embed is nil and it cannot initialize that field.
json.Unmarshal(b, out)

// Manually allocated embed would allow this to work:
out.embed = &embed{}
json.Unmarshal(b, out)

We tried ignoring that field entirely, and that broke in various ways, so I think it seems pretty clear we should move on to Joe’s option 3: return an error if we want to fill out embed but are now prevented from doing that.

Ignoring the field is much more surprising than I realized: it’s a silent behavioral change. Option 3 should only change some former accidental successes into error results, which is usually preferred anyway.

@dsnet, can you look into a fix for encoding/json? (If not let me know and I will make time.) Thanks.