39

So I'm trying to parse a JSON response. It can be multiple levels deep. This is what I did:

var result map[string]interface{}
json.Unmarshal(apiResponse, &result)

Firstly, is this the right way to do it?

Lets say the response was as follows:

{
  "args": {
            "foo": "bar"
          }
}

To access key foo, I saw a playground doing this:

result["args"].(map[string]interface{})["foo"]

Here, what is the .() notation? Is this correct?

2 Answers 2

60

The notation x.(T) is called a Type Assertion.

For an expression x of interface type and a type T, the primary expression x.(T) asserts that x is not nil and that the value stored in x is of type T.

Your example:

result["args"].(map[string]interface{})["foo"]

It means that the value of your results map associated with key "args" is of type map[string]interface{} (another map with string keys and any values). And you want to access the element of that map associated with the key "foo".

If you know noting about the input JSON format, then yes, you have to use a generic map[string]interface{} type to process it. If you know the exact structure of the input JSON, you can create a struct to match the expected fields, and doing so you can unmarshal a JSON text into a value of your custom struct type, for example:

type Point struct {
    Name string
    X, Y int
}

func main() {
    in := `{"Name":"center","X":2,"Y":3}`

    pt := Point{}
    json.Unmarshal([]byte(in), &pt)

    fmt.Printf("Result: %+v", pt)
}

Output:

Result: {Name:center X:2 Y:3}

Try it on the Go Playground.

Modeling your input

Your current JSON input could be modelled with this type:

type Data struct {
    Args struct {
        Foo string
    }
}

And accessing Foo (try it on the Go Playground):

d := Data{}
json.Unmarshal([]byte(in), &d)
fmt.Println("Foo:", d.Args.Foo)
Sign up to request clarification or add additional context in comments.

3 Comments

Also, if foo is of type interface{}, what's the difference between doing string(foo) and foo.(string)?
@SreejithRamakrishnan I don't think there is any significant performance hit as if you use a custom struct, it is also populated via reflection.
@SreejithRamakrishnan And yes, there is difference between stirng(foo) and foo.(string). First is a conversion, the latter is a type assertion. Type assersion can only be used on interface types (interface{} is an empty interface which qualifies), and for the rules of conversion read the spec: Conversions. If your case you can't use conversion only type assertion.
12

struct is the best option, but if you insist, you can add a type declaration for a map, then you can add methods to help with the type assertions:

package main
import "encoding/json"

type dict map[string]interface{}

func (d dict) d(k string) dict {
   return d[k].(map[string]interface{})
}

func (d dict) s(k string) string {
   return d[k].(string)
}

func main() {
   apiResponse := []byte(`{"args": {"foo": "bar"}}`)
   var result dict
   json.Unmarshal(apiResponse, &result)
   foo := result.d("args").s("foo")
   println(foo == "bar")
}

https://golang.org/ref/spec#Type_declarations

1 Comment

Helper method for slice: func (d dict) a(k string) []interface{} { return d[k].([]interface{}) }

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.