-
Notifications
You must be signed in to change notification settings - Fork 18k
encoding/json: promoted Unmarshal method on embedded field caused confusion #39470
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
In |
@hantmac did u run the program ?
and uncommenting the Object unmarshalling results with :
|
Of course, executes just fine because you did not handle the error in |
@hantmac
|
@talbspx Sorry,I misunderstood what you meant. But, the reason that you can't get what you expect is the order of execution of the code is |
@talbspx thank you for this question and welcome to the Go project! @talbspx, the technicality that's subtle here is that your embedded struct Nested's UnmarshalJSON method is promoted when you used embedded structs and is instead being used which means that without a top-level (*Object).UnmarshalJSON method, unmarshalling to Num will never work. You can verify this behavior by adding this single line anywhere in your code var _ json.Unmarshaler = (*Object)(nil) To fix your problem there are a couple of ways: package main
import (
"encoding/json"
"fmt"
"time"
)
var testJSON = `{"num":5,"duration":"5s"}`
type Nested struct {
Dur time.Duration `json:"duration"`
}
func (obj *Object) UnmarshalJSON(data []byte) error {
tmp := struct {
Dur string `json:"duration"`
Num int `json:"num"`
}{}
if err := json.Unmarshal(data, &tmp); err != nil {
return err
}
dur, err := time.ParseDuration(tmp.Dur)
if err != nil {
return err
}
obj.Dur = dur
obj.Num = tmp.Num
return nil
}
type Object struct {
Nested
Num int `json:"num"`
}
var _ json.Unmarshaler = (*Object)(nil)
func main() {
obj := Object{}
_ = json.Unmarshal([]byte(testJSON), &obj)
fmt.Printf("result: %+v \n", obj)
} b) Add a custom time unmarshaller https://play.golang.org/p/ALzsum_E-hw package main
import (
"encoding/json"
"fmt"
"time"
)
var testJSON = `{"num":5,"duration":"5s"}`
type customTimeDuration time.Duration
type Nested struct {
Dur customTimeDuration `json:"duration"`
}
func (ctd *customTimeDuration) UnmarshalJSON(b []byte) error {
var durStr string
if err := json.Unmarshal(b, &durStr); err != nil {
return err
}
dur, err := time.ParseDuration(durStr)
if err == nil {
*ctd = customTimeDuration(dur)
}
return err
}
type Object struct {
Nested
Num int `json:"num"`
}
func main() {
obj := Object{}
_ = json.Unmarshal([]byte(testJSON), &obj)
fmt.Printf("result: %+v \n", obj)
} Hope that resolves your questions, but the tip you can use to verify whether a promoted method will be invoked is with type assertions, and also remembering the relationship between promoted methods and embedded fields. The type assertion will be a nice guide var _ json.Unmarshaler = (*Object)(nil)
var _ json.Unmarshaler = (*Nested)(nil) that method will also later on help with catching bugs that manifest at runtime and might not easily be caught because they are embedded into lots of logic. Some 3 years ago when someone on my team encountered similar problems and after I examined Go bugs I wrote this guide that could be helpful https://medium.com/@odeke_et/compile-type-assertions-to-the-rescue-6ddab4b8398b Asking questionsWe use the Go issue tracker for actual bugs in Go. In the future, please ask questions on any of these resources https://github.com/golang/go/wiki/Questions
Thank you again, and cheers! |
@odeke-em |
What version of Go are you using (
go version
)?Does this issue reproduce with the latest release?
yes
What operating system and processor architecture are you using (
go env
)?go env
OutputWhat did you do?
I wanted to add custom logic for json unmarshaling of a nested struct.
https://play.golang.org/p/RruGGGiWmN-
What did you expect to see?
i expected to have a complete object deserialized from the json string.
in the playground example, the field Num should have been valued as 5
along with the nested Dur field.
What did you see instead?
only the nested struct deserialized.
in the playground example, only the nested Dur field was deserialized correctly and
the composite object fields , Num , are zero valued.
The text was updated successfully, but these errors were encountered: