Maxim Samburskiy Maxim Samburskiy - 3 months ago 15
JSON Question

Decode both map and array json

External API returns empty array if no items:

{"items":[]}


...or map with items:

{"items":{"1": {...}, "2": {...}}}


How can I decode both of them? I tried using this struct:

var response struct {
Items map[string]Item
Array []Item `json:"items"`
}


But it doesn't work.

UPDATE: Best if both (array and object) will produce a
map[string]Item
(empty and filled)

Answer

If you need to unmarshall to a variable type, the easiest way is to unmarshal into a map[string]interface{} and type-assert (or in this case, type-switch) your way out.

func Unmarshal(data []byte) (map[string]Item, error) {
    var d struct {
        Items interface{} `json:"items"`
    }
    if err := json.Unmarshal(data, &d); err != nil {
        return nil, err
    }
    switch dv := d.Items.(type) {
    case []interface{}:
        if len(dv) == 0 {
            return nil, nil
        }
    case map[string]interface{}:
        m := make(map[string]Item)
        for k, v := range dv {
            m[k] = Item(v)
        }
        return m, nil
    }
    // fallthrough return if different type, or non-empty array
    // Could have put this in a default case, but this catches non-empty arrays too
    return nil, fmt.Errorf("unexpected type in json")
}

Here's an example showing it works for both of your provided examples: https://play.golang.org/p/c0oZX2-xpN

Comments