Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

json.Unmarshal fails when embedded type has UnmarshalJSON

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.

like image 599
David Brophy Avatar asked Apr 16 '15 06:04

David Brophy


1 Answers

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.

like image 194
ANisus Avatar answered Nov 15 '22 09:11

ANisus