-
Notifications
You must be signed in to change notification settings - Fork 18k
encoding/json: cannot unmarshal into unexported embedded pointer types #21357
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
Sorry if this has been raised before, I'm not yet very familiar with this issue. What if we slightly change the problem to this: package other
type embed struct{ X int }
type S struct { *embed }
func NewS(x int) *S {
return &S{&embed{x}}
}
package main
in := other.NewS(5)
out := &other.S{}
b, _ := json.Marshal(in) // string(b) == `{"X": 5}`
json.Unmarshal(b, out) I assume I think choice 1 has the advantage that unmarshalling into an embedded field would unconditionally not work instead of working only if some properties you might not have control over is satisfied. |
It's not |
Do you mean in the line above? You're right, I mistyped. Edited. |
I support the solution of returning an error. Users will need to either explicitly construct the embed instance if they do care about unmarshaling values into it, or if they don't, they can tag it with |
Change https://golang.org/cl/53643 mentions this issue: |
Maybe we should just ignore/reject pointers to unexported embedded types unconditionally. The "only if they're allocated" is a bit surprising. |
Unless I am mistaken, this would also break something like: type a struct {
B string
}
type Parent struct {
a *a `json:"A"`
} right? I think this is probably more common than an embedded unexported pointer. |
In your example, it's the @rsc, are you proposing ignoring pointers to unexported embedded types only for unmarshal or marshal+unmarshal? |
my mistake, it looks like json pkg ignores |
Change https://golang.org/cl/60410 mentions this issue: |
Yes, I was proposing to ignore pointers to unexported embedded types entirely. |
(For both Marshal and Unmarshal, unconditionally, no matter what their values.) |
I'll implement the change and see what breaks as a result of this (hopefully nothing). |
The previous logic was overly complicated, generated suboptimally encoded struct type descriptors, and mishandled embeddings of predeclared universal types. Fixes #21122. Fixes #21353. Fixes #21696. Fixes #21702. Updates #21357. Change-Id: If34761fa6dbe4af2af59dee501e7f30845320376 Reviewed-on: https://go-review.googlesource.com/60410 Run-TryBot: Matthew Dempsky <[email protected]> TryBot-Result: Gobot Gobot <[email protected]> Reviewed-by: David Crawshaw <[email protected]>
Change https://golang.org/cl/76851 mentions this issue: |
I'm sad to discover that this breaks many packages in For example in dataflow/v1b3/dataflow-gen.go:L264-L276: func (s *ApproximateProgress) UnmarshalJSON(data []byte) error {
type noMethod ApproximateProgress
var s1 struct {
PercentComplete gensupport.JSONFloat64 `json:"percentComplete"`
*noMethod
}
s1.noMethod = (*noMethod)(s)
if err := json.Unmarshal(data, &s1); err != nil {
return err
}
s.PercentComplete = float64(s1.PercentComplete)
return nil
} The good news is that the fix is trivial: just export \cc @zombiezen Who owns the generator for |
Effectively, @jba and I do. We can roll out a generated code fix pretty quickly. |
Can you make the suggested change? Essentially I can review the CL. |
I happen to be working in the neighborhood. I'll do it. |
https://code-review.googlesource.com/19531 is the fix to the generator, just have to regenerate the packages. |
https://code-review.googlesource.com/19571 regenerates the code. |
As of 6be1c09, the json implementation is broken with respect to duplicate fields. Same-named fields at the same nesting level should annihilate each other, as this playground example shows. But at tip, the json-encoded string is |
If the new behavior is to consistently ignore any exported fields from embedded pointers to unexported structs, then this seems to be working as intended. It seems inconsistent to me if we ignore unmarshaling exported fields from embedded pointers to unexported structs, but still consider such fields for field annihilation. |
There's an inconsistency either way. Whatever the ability of the reflect package to allocate certain values, a duplicate field cannot be selected from a struct. That's a fact about the type, not about the value. As it stands now, encoding/json behaves inconsistently w.r.t. the type. With
|
I should say that this is obviously an edge case, and I have no stake in the answer. I just want to make sure we think about it. |
CL 76851 was both mailed and submitted after the freeze, and it is breaking things in questionable ways, such that we're not sure what the right semantics are. Given all that, it seems like the right thing for Go 1.10 is to revert CL 76851, to restore the Go 1.9 behavior. |
It’s not possible to revert to Go1.9 behavior without also reverting the compiler fix that CL/60410 addresses. |
I looked through this again. This is really unfortunate. Going back to Joe's original example:
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. |
Can do. |
Change https://golang.org/cl/82135 mentions this issue: |
The new policy fixed all the known breakages inside Google (with the exception of googleapis/google-cloud-go#813) without any changes to user code. |
DO NOT SUBMIT: test still fails because of bug in encoding/json fix. As of Go 1.10, a bug in reflect has been fixed, so it is no longer possible to create pointers to embedded, unexported structs. As a result, encoding/json's output has changed in these cases. (See golang/go#21357). We make the same change in our fields package, for consistency. Fixes #813. Change-Id: I836ddc6f9c4e8ef9e37643b66f287f2916799c93
this commit fixes a runtime panic "cannot set embedded pointer to unexported struct: sampling.samplingRule" see golang/go#21357
A consequence of fixing #21353 is that you cannot round-trip marshal/unmarshal the following:
The problem is that the
json
package had been relying on a bug within thereflect
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:I believe the 3rd option is the least surprising.
\cc @zombiezen @adams-sarah @cybrcodr @jba @pongad
The text was updated successfully, but these errors were encountered: