Reputation: 2001
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)
Upvotes: 1
Views: 1356
Reputation:
Try this working code (A):
package main
import (
"encoding/json"
"fmt"
)
type Item int
type response struct {
Items map[string]Item `json:"Items"`
Array []Item `json:"Array"`
}
func main() {
var d response
err := json.Unmarshal([]byte(`{"Items":{"A":1,"B":2,"C":3},"Array":[]}`), &d)
if err != nil {
panic(err)
}
fmt.Println(d)
}
output:
{map[C:3 A:1 B:2] []}
Try this working code (B):
package main
import (
"encoding/json"
"fmt"
)
type Item int
type response struct {
Items map[string]Item `json:"Items"`
//Array []Item `json:"Array"`
}
func main() {
var d response
err := json.Unmarshal([]byte(`{"Items":{"A":1,"B":2,"C":3},"Array":[]}`), &d)
if err != nil {
panic(err)
}
fmt.Println(d)
}
output:
{map[C:3 A:1 B:2]}
You may use json.Marshal
and json.Unmarshal
, like this working code (C):
package main
import (
"encoding/json"
"fmt"
)
type Item int
type response struct {
Items map[string]Item `json:"Items"`
Array []Item `json:"Array"`
}
func main() {
var test = response{
Items: map[string]Item{"A": 1, "B": 2, "C": 3},
Array: []Item{},
}
body, err := json.Marshal(test)
if err != nil {
panic(err)
}
fmt.Println(string(body)) // {"Items":{"A":1,"B":2,"C":3},"Array":[]}
var d response
err = json.Unmarshal(body, &d)
if err != nil {
panic(err)
}
fmt.Println(d)
}
output:
{"Items":{"A":1,"B":2,"C":3},"Array":[]}
{map[A:1 B:2 C:3] []}
You may Unmarshal {"A":1,"B":2,"C":3}
to map[A:1 B:2 C:3]
and "[1,2,3]"
to [1 2 3]
, like this working code (D):
package main
import (
"encoding/json"
"fmt"
)
type Item int
type response1 map[string]Item
type response2 []Item
func main() {
var d response1
err := json.Unmarshal([]byte(`{"A":1,"B":2,"C":3}`), &d)
if err != nil {
panic(err)
}
fmt.Println(d) // map[A:1 B:2 C:3]
var d2 response2
err2 := json.Unmarshal([]byte(`[1,2,3]`), &d2)
if err2 != nil {
panic(err2)
}
fmt.Println(d2) // [1 2 3]
}
output:
map[A:1 B:2 C:3]
[1 2 3]
Upvotes: 0
Reputation: 10158
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
Upvotes: 1