invalid operation: type interface {} does not support indexing
Asked Answered
D

3

5

I'm new to the golang and I have problem while reading the nested JSON response.

var d interface{}
json.NewDecoder(response.Body).Decode(&d)
test :=d["data"].(map[string]interface{})["type"]

response.Body looks like this

{
    "links": {
      "self": "/domains/test.one"
    },
    "data": {
        "type": "domains",
        "id": "test.one",
        "attributes": {
            "product": " Website",
            "package": "Professional",
            "created_at": "2016-08-19T11:37:01Z"
        }
    }
}

The Error I'm getting is this:

invalid operation: d["data"] (type interface {} does not support indexing)
Devlen answered 11/12, 2017 at 6:42 Comment(3)
This is a pretty good resource gobyexample.com/jsonSesquicentennial
@jeff i'm using interface assuming that i don't know the structure of response.Devlen
@Sesquicentennial it works but assuming if i have to parse nested element this solution will be complex. do you have any alternate solution ?Devlen
S
19

d is of type interface{}, so you cannot index it like d["data"], you need another type assertion:

test := d.(map[string]interface{})["data"].(map[string]interface{})["type"]
fmt.Println(test)

Then it will work. Output will be "domains". See a working example on the Go Playground.

Also note that if you declare d to be of type map[string]interface{}, you can spare the first type assertion:

var d map[string]interface{}
if err := json.NewDecoder(response.Body).Decode(&d); err != nil {
    panic(err)
}
test := d["data"].(map[string]interface{})["type"]
fmt.Println(test)

Output is the same. Try this one on the Go Playground.

If you need to do these and similar operations many times, you may find my github.com/icza/dyno library useful (whose primary goal is to aid working with dynamic objects).

Snowdrop answered 11/12, 2017 at 8:25 Comment(2)
Tip for newbies like me: To understand why this code works, one should understand how Decode works, which is related to unmarshal. The json.Unmarshal doc explains how it stores data.Catto
> if you declare d to be of type map[string]interface{}, you can spare the first type assertion --> you MUST spare the first type assertionMitinger
C
0

You need some tricks to handle your situation.
Like using reflect and you may ref Marshall&&UnMarshall code about bson.M in golang mongo driver mgo

code sample using reflect decode nested as following:

package main

import (
    "encoding/json"
    "fmt"
    "reflect"
)

func main() {
    keys := []string{"hello", "world", "dude", "kind", "cool"}
    a := make(map[string]interface{})
    a[keys[4]] = "perfect"
    b := make(map[string]interface{})
    b[keys[3]] = a
    c := make(map[string]interface{})
    c[keys[2]] = b
    d := make(map[string]interface{})
    d[keys[1]] = c
    e := make(map[string]interface{})
    e[keys[0]] = d
    fmt.Println(e)

    if buf, err := json.Marshal(e); nil == err {
        dd := make(map[string]interface{})
        err = json.Unmarshal(buf, &dd)
        if nil != err {
            fmt.Println("failed", err)
        }

        for k, v := range dd {
            travel(dd, k, v)
        }
        fmt.Println(dd)

    } else {
        fmt.Println("failed marshal")
    }
}

func travel(dd map[string]interface{}, key string, value interface{}) {
    vv := reflect.ValueOf(value)
    switch vv.Kind() {
    case reflect.Map:
        m := value.(map[string]interface{})
        dd[key] = m
        for k, v := range m {
            travel(m, k, v)
        }
    case reflect.String:
        dd[key] = value.(string)
    }
}
Collapse answered 11/12, 2017 at 8:23 Comment(4)
This question has nothing to do with BSON or Mongo. If you want to reference something for using reflection in this case, you might as well read the source of the json package since it's actually relevant.Dina
I recommend to read bson.M cause the questioner said in the comments, nested interface{}, and I think bson.M is an example for decode nested interface{}Collapse
Handling of nesting is simple recursion. JSON does it as well.Dina
I am new to go and familiar with bson.M, sorry to waste your time.Collapse
E
-1

You can do this also:

var inputJson string = "..."

var decoded map[string]map[string]interface{}

json.Unmarshal([]byte(inputJson), &decoded)

test := decoded["data"]["type"]

fmt.Println(test)
// output: domains
Easterling answered 22/7, 2020 at 17:13 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.