I'm trying to unmarshal a struct that has an embedded type. When the embedded type has an UnmarshalJSON method, the unmarshaling of the outer type fails:
https://play.golang.org/p/Y_Tt5O8A1Q
package main
import (
"fmt"
"encoding/json"
)
type Foo struct {
EmbeddedStruct
Field string
}
func (d *Foo) UnmarshalJSON(from []byte) error {
fmt.Printf("Foo.UnmarshalJSON\n")
type Alias Foo
alias := &Alias{}
if err := json.Unmarshal(from, alias); err != nil {
return fmt.Errorf("Error in Foo.UnmarshalJSON: json.Unmarshal returned an error:\n%v\n", err)
}
*d = Foo(*alias)
return nil
}
type EmbeddedStruct struct {
EmbeddedField string
}
func (d *EmbeddedStruct) UnmarshalJSON(from []byte) error {
fmt.Printf("EmbeddedStruct.UnmarshalJSON\n")
type Alias EmbeddedStruct
alias := &Alias{}
if err := json.Unmarshal(from, alias); err != nil {
return fmt.Errorf("Error in EmbeddedStruct.UnmarshalJSON: json.Unmarshal returned an error:\n%v\n", err)
}
*d = EmbeddedStruct(*alias)
return nil
}
func main() {
data := `{"EmbeddedField":"embeddedValue", "Field": "value"}`
foo := &Foo{}
json.Unmarshal([]byte(data), foo)
fmt.Printf("Foo: %v\n", foo)
if foo.EmbeddedField != "embeddedValue" {
fmt.Printf("Unmarshal didn't work, EmbeddedField value is %v. Should be 'embeddedValue'\n", foo.EmbeddedField)
}
if foo.Field != "value" {
fmt.Printf("Unmarshal didn't work, Field value is %v. Should be 'value'\n", foo.Field)
}
}
The output is:
Foo.UnmarshalJSON
EmbeddedStruct.UnmarshalJSON
Foo: &{{embeddedValue} }
Unmarshal didn't work, Field value is . Should be 'value'
... so both custom unmarshal functions ran. The value from the embedded struct is correct, but the value from the outer struct is lost.
If we simply remove the EmbeddedStruct.UnmarshalJSON method, it works as expected.
Am I doing something wrong? Is this expected? Or a bug? I'm sure there's a way I can tweak my UnmarshalJSON methods to get it working.
It is expected.
When you create the alias:
type Alias Foo
Alias
will not inherit the methods of Foo
since it is a different type with a different method set, which is what you wanted to achieve to avoid an infinite recursion.
However, the embedded EmbeddedStruct
's UnmarshalJSON
method will instead be promoted!
So, Alias
will have an UnmarshalJSON
method that will only unmarshal EmbeddedStruct
's value, instead of using the default unmarshalling that you desired.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With