[...] trying to access just one field (out of the many available).
For this concrete use case I would use a library to query and access to a single value in a known path like:
https://github.com/jmespath/go-jmespath
In the other hand, if you're practicing how to access nested values in a JSON, I would recommend you to give a try to write a recursive function that follows a path in an unknown structure the same way (but simple) like go-jmespath does.
Ok, I challenged myself and spent an hour writing this. It works. Not sure about performance or bugs and it's really limited :)
https://play.golang.org/p/dlIsmG6Lk-p
package main import ( "encoding/json" "errors" "fmt" "strings" ) func main() { // I Just added a bit more of data to the structure to be able to test different paths fileContent := []byte(` {"results": [ {"times": [ 1, 2, 3, 4 ]}, {"times2": [ 5, 6, 7, 8 ]}, {"username": "rosadabril"}, {"age": 42}, {"location": [41.5933262, 1.8376757]} ], "more_results": { "nested_1": { "nested_2":{ "foo": "bar" } } } }`) var content map[string]interface{} if err := json.Unmarshal(fileContent, &content); err != nil { panic(err) } // some paths to test valuePaths := []string{ "results.times", "results.times2", "results.username", "results.age", "results.doesnotexist", "more_results.nested_1.nested_2.foo", } for _, p := range valuePaths { breadcrumbs := strings.Split(p, ".") value, err := search(breadcrumbs, content) if err != nil { fmt.Printf("\nerror searching '%s': %s\n", p, err) continue } fmt.Printf("\nFOUND A VALUE IN: %s\n", p) fmt.Printf("Type: %T\nValue: %#v\n", value, value) } } // search is our fantastic recursive function! The idea is to search in the structure in a very basic way, for complex querying use jmespath func search(breadcrumbs []string, content map[string]interface{}) (interface{}, error) { // we should never hit this point, but better safe than sorry and we could incurr in an out of range error (check line 82) if len(breadcrumbs) == 0 { return nil, errors.New("ran out of breadcrumbs :'(") } // flag that indicates if we are at the end of our trip and whe should return the value without more checks lastBreadcrumb := len(breadcrumbs) == 1 // current breadcrumb is always the first element. currentBreadcrumb := breadcrumbs[0] if value, found := content[currentBreadcrumb]; found { if lastBreadcrumb { return value, nil } // if the value is a map[string]interface{}, go down the rabbit hole, recursion! if aMap, isAMap := value.(map[string]interface{}); isAMap { // we are calling ourselves popping the first breadcrumb and passing the current map return search(breadcrumbs[1:], aMap) } // if it's an array of interfaces the thing gets complicated :( if anArray, isArray := value.([]interface{}); isArray { for _, something := range anArray { if aMap, isAMap := something.(map[string]interface{}); isAMap && len(breadcrumbs) > 1 { if v, err := search(breadcrumbs[1:], aMap); err == nil { return v, nil } } } } } return nil, errors.New("woops, nothing here") }
interface{}because you don't want to pre-declare a struct, you have to do the "especially ugly" thing. If you pre-declare a struct you don't have to. It's a trade-off.