andig
andig

Reputation: 13868

How to fix json: cannot unmarshal object into Go value of type []json.RawMessage

I want to unmarshal

var j = []byte(`[{"major":1},{"minor":0}]`)

into

type Version struct {
    Major int `json:"major"`
    Minor int `json:"minor"`
}

using custom unmarshaler by looping the inner slice:

func (h *Version) UnmarshalJSON(b []byte) error {
    var wrapper []json.RawMessage

    err := json.Unmarshal(b, &wrapper)
    if err == nil {
        for _, v := range wrapper {
            if err = json.Unmarshal(v, &h); err != nil {
                break
            }
        }
    }

    return err
}

The inner UnmarshalJSON triggers

json: cannot unmarshal object into Go value of type []json.RawMessage

which is strange because the target is a *Version. What is wrong here? Play: https://play.golang.org/p/Av59IkYTioS

Upvotes: 2

Views: 11749

Answers (1)

icza
icza

Reputation: 417402

The "inner" unmarshal call will recursively call Version.UnmarshalJSON():

json.Unmarshal(v, &h)

And your error comes from the recursive call: you try to unmarshal {"major":1} into []json.RawMessage.

You do not want to call Version.UnmarshalJSON() recursively, so create a new type that strips all methods, including the UnmarshalJSON():

    type version Version
    h2 := (*version)(h)
    for _, v := range wrapper {
        if err = json.Unmarshal(v, &h2); err != nil {
            break
        }
    }

With this change it works, and adding fmt.Println(msg) the output will be (try it on the Go Playground):

<nil>
{1 0}

Normally this would cause a stack overflow error, but since your second call errors, the recursion breaks. See related: Call json.Unmarshal inside UnmarshalJSON function without causing stack overflow

Upvotes: 2

Related Questions