-1

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.

Jonathan Hall
  • 75,165
  • 16
  • 143
  • 189
Gerb
  • 883
  • 12
  • 31
  • 3
    You don't need reflection here. The value is defined as `map[string]interface{}` so you *know* that the keys are strings (and even if you didn't you could just use type assertions or type switches). – Peter Dec 06 '19 at 14:43
  • 1
    You should use type assertions instead of reflection. Check this answer: [Taking a JSON string, unmarshaling it into a map, editing, and marshaling it into a byte slice seems more complicated then it should be](https://stackoverflow.com/questions/28877512/taking-a-json-string-unmarshaling-it-into-a-mapstringinterface-editing-an/28878037#28878037). – icza Dec 06 '19 at 14:43
  • You can't do anything with JSON using reflection; JSON is just plain text, a slice of bytes. You're just wanting to iterate over a `map`, which as Peter pointed out doesn't require reflection. – Adrian Dec 06 '19 at 15:45

1 Answers1

2

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()
   }
}
Burak Serdar
  • 46,455
  • 3
  • 40
  • 59
  • Thank you! I knew I wasn't doing something right. Reflection felt too cumbersome for something as easy as JSON manipulation. This solved my issue. Go is constantly surprising me with its awesomeness with its language and the community that supports it. – Gerb Dec 06 '19 at 17:09