JSON Unmarshalling with different keys

go, json, struct

Solution

icza's approach is good, and better if this implementation could match stdlib's interface.

I suggest implementing `json.Unmarshaler`:

// it can't get c itself.
type MyJson struct {
    A int `json:"a"`
    B int `json:"b"`
}

func (j *MyJson) UnmarshalJSON(b []byte) error {
    type Alias MyJson
    realValue := struct {
        *Alias
        C int `json:"c"`  // <- now it can accept 'c' value
    }{(*Alias)(j), 0}

    if err := json.Unmarshal(b, &realValue); err != nil {
        return err
    } else if realValue.C != 0 {
        // if C has value, overwrite A
        realValue.A = realValue.C
    }

    return nil
}

And just decode it using `json.Unmarshal`.

Problem

I'm trying to unmarshal some JSON from different sources that may have different keys. For instance, I may have: ``` { "a": 1, "b": 2 } ``` Or I may have: ``` { "c": 1, "b": 2 } ``` In this case, I can guarantee that "b" will be there. However, I want "a" and "c" to be represented the same way. In effect, what I want is: ``` type MyJson struct { Init int `json:"a",json:"c"` Sec int `json:"b" } ``` Basically I want the unmarshaller to look for either key and set it as `Init`. Now this doesn't actually work (or I wouldn't be posting). Unmarshalling the first gives me what I want, while the second sets `Init` to 0. My ideal option would be to unmarshal to a struct, with one of two possibilities: - I represent the struct with multiple tags as above - I define multiple structs, but am able to choose which to unmarshal to I tried to implement number 2 by making two different structs and a map, but it seems I can't make a map with a type as the second value: ``` type MyJson1 struct { Init int `json:"a"` Sec int `json:"b"` } type MyJson2 struct { Init int `json:"c"` Sec int `json:"b"` } ``` Is there a way to define a set of structs that behaves like an interface? That is, they all have the same fields, but are defined differently. Or maybe there's another way. I could make the fields unmarshalling "a" and "c" other fields, then set `Init` accordingly. But this doesn't scale beyond a couple of variants. Thanks!

Original source