6

I'm writing a websocket client in Go. I'm receiving the following JSON from the server:

{"args":[{"time":"2013-05-21 16:57:17"}],"name":"send:time"}

I'm trying to access the time parameter, but just can't grasp how to reach deep into an interface type:

 package main;
 import "encoding/json"
 import "log"
 func main() {
    msg := `{"args":[{"time":"2013-05-21 16:56:16", "tzs":[{"name":"GMT"}]}],"name":"send:time"}`
    u := map[string]interface{}{}
    err := json.Unmarshal([]byte(msg), &u)
    if err != nil {
        panic(err)
    }
    args := u["args"]
    log.Println( args[0]["time"] )   // invalid notation...
}

Which obviously errors, since the notation is not right:

   invalid operation: args[0] (index of type interface {})

I just can't find a way to dig into the map to grab deeply nested keys and values.

Once I can get over grabbing dynamic values, I'd like to declare these messages. How would I write a type struct to represent such complex data structs?

ojosilva
  • 1,984
  • 2
  • 15
  • 21

3 Answers3

10

You may like to consider the package github.com/bitly/go-simplejson

See the doc: http://godoc.org/github.com/bitly/go-simplejson

Example:

time, err := json.Get("args").GetIndex(0).String("time")
if err != nil {
    panic(err)
}
log.Println(time)
Rick-777
  • 9,714
  • 5
  • 34
  • 50
Codefor
  • 1,318
  • 2
  • 13
  • 22
  • Wow, real nice. Just wish I had something as easy as that for working with YAML also, although Gypsy looks promising. – ojosilva May 23 '13 at 16:53
8

The interface{} part of the map[string]interface{} you decode into will match the type of that field. So in this case:

args.([]interface{})[0].(map[string]interface{})["time"].(string)

should return "2013-05-21 16:56:16"

However, if you know the structure of the JSON, you should try defining a struct that matches that structure and unmarshal into that. Ex:

type Time struct {
    Time time.Time      `json:"time"`
    Timezone []TZStruct `json:"tzs"` // obv. you need to define TZStruct as well
    Name string         `json:"name"`
}

type TimeResponse struct {
    Args []Time         `json:"args"`
}

var t TimeResponse
json.Unmarshal(msg, &t)

That may not be perfect, but should give you the idea

cthom06
  • 9,389
  • 4
  • 36
  • 28
  • That worked, thanks. But how would I turn this into a type struct, then access the data? – ojosilva May 21 '13 at 16:35
  • The struct is brilliant stuff! I just don't get how the `json:"key"` string fits into the syntax of a Go type struct, although I understand that it it's telling the `json.Unmarshall()` func where to fit each key-value pair. – ojosilva May 23 '13 at 16:01
  • @ojosilva It's honestly a bit of a hack I think. Struct fields can be "tagged" with a string, which can be accessed via reflection. The json package checks those tags for json:options – cthom06 May 23 '13 at 16:17
1

I'm extremely new to Golang coming from Python, and have always struggled with encode/decoding json. I found gjson at https://github.com/tidwall/gjson, and it helped me immensely:

package main
    
import "github.com/tidwall/gjson"

func main() {
    msg := (`{"args":[{"time":"2013-05-21 16:56:16", "tzs":[{"name":"GMT"}]}],"name":"send:time"}`)
    value := gjson.Get(msg, "args.#.time")
    println(value.String())
}
-----------------------
["2013-05-21 16:56:16"]

Additionally, I noticed the comment of how to convert into Struct

package main
    
import (
        "encoding/json"
        "fmt"
    )

type msgFormat struct {
    Time string       `json:"time"`
    Tzs  msgFormatTzs `json:"tzs"`
    Name string       `json:"name"`
}

type msgFormatTzs struct {
    TzsName string `json:"name"`
}


func main() {

    msg := (`{"args":[{"time":"2013-05-21 16:56:16", "tzs":[{"name":"GMT"}]}],"name":"send:time"}`)

    r, err := json.Marshal(msgFormatTzs{msg})

    if err != nil {
        panic(err)
    }

    fmt.Printf("%v", r)
}

Try on Go playground

sixfears7
  • 66
  • 4