Search code examples
jsongoreflect

Iterating over json using reflection


I'm trying to consume a rest endpoint in my golang project. The JSON structure is pretty large and is semi-structured, so I'm using reflection to iterate over it and grab the information that I am interested in.

Here is how I'm unmarshal-ing the response:

    var m map[string]interface{}
    json.Unmarshal(bytes, &m)

But the part I'm stuck at is - I'm iterating through a slice of maps (i think) but I'm unable to get the keys or values of the map. Here is the code in question.

    if val, ok := m["messages"]; ok {
        s := reflect.ValueOf(val)

        if s.Kind() == reflect.Slice {
            for i := 0; i < s.Len(); i++ {
                item := s.Index(i)

                fmt.Println("VALUE = ", item.Elem())
                fmt.Println("KIND = ", item.Kind())
            }
        }
        return
    }

When I run the code the value that is displayed looks like a map:

map[decoration_stats:<nil> highlight_ranges:map[] index:graylog_7 message:map[_id:49272694-1834-11ea-8928-0242ac120004 docker:{"container_id":"0f9d97722c25240c6f99487b247b2416177a749de47d661cd661334514e0e74f"} facility:fluentd gl2_message_id:01DVDSM9VSDQ5PF81T4C31NSH6....

And the kind is:

KIND =  interface

I tried various things like:

        for _, e := range val.MapKeys() {
            v := val.MapIndex(e)
            fmt.Println(v)
        }

But the code panics with:

panic: reflect: call of reflect.Value.MapKeys on interface Value

Sorry, I'm somewhat new to golang but have used other static typed language, mainly Java, when it comes to any reflection type programming.

My question is how to can I convert this interface to a map or some concrete type so that I can use it. Any help would be greatly appreciated.


Solution

  • Using reflection is an inefficient way to do this. JSON unmarshal, when used with an interface (and map[string]interface{}) produces a limited set of types, and you can use type assertions or a type-switch to deal with it:

    if val, ok := m["messages"]; ok {
       switch v:=val.(type) {
          case map[string]interface{}: // JSON object
            for key, value:=range v {
            }
          case []interface{}: // JSON array
            for i,node:=range v {
            }
          case string: // string value
          case float64: // numeric value
          case bool: // boolean value
          case json.Number: // If you use json.Decoder with UseNumber()
       }
    }