1

I have a JSON structure such as:

    {
       "persons":[
          {
             "name":"mark",
             "surname":"zuckerberg",
             "data":[
                {
                   "userid":"09210",
                   "username":"mark290",
                   "registered_date":"10-01-2017"
                },

                {
                   "userid":"092240",
                   "username":"mark291",
                   "registered_date":"11-01-2017"
                }
             ]
          },

          {
             "name":"bill",
             "surname":"clinton",
             "data":[
                {
                   "userid":"0442340",
                   "username":"billy",
                   "registered_date":"10-01-2000"
                },

                {
                   "userid":"89472894",
                   "username":"billary",
                   "registered_date":"11-01-2015"
                }
             ]
          }
       ]
    }

I need to fetch each username for each person. Currently, in Go, which I am a total newbie at, I can get only the single JSONS within "data", with this snippet:

    j := []byte(data)
    var f interface{}
    err := json.Unmarshal(j, &f)
    map := f.(map[string]interface{})
    for _,item := range map["persons"].([]interface{}) {
        fmt.Printf("%v", item.(map[string]interface{})["data"])
    fmt.Println(err)

I struggle to understand how to dive within each JSON struct within data in order to get each key,value pair.

kozmo
  • 4,024
  • 3
  • 30
  • 48
  • 1
    Consider using concrete types instead of `interface{}`. – mkopriva Oct 05 '21 at 16:38
  • Declare a Go type that matches the structure of your data. Unmarshal to a value of that type. See [example](https://stackoverflow.com/a/64186764/5728991). –  Oct 05 '21 at 16:40
  • But I forgot to mention that in my case the JSON structure is not always the same. It is not consistent. I assumed that in this case it is better to use interfaces instead of structs. – Lorenzo Romani Oct 05 '21 at 16:51
  • https://github.com/mitchellh/mapstructure "data format is not consistent" is pretty much the exact pitch for mapstructure. The problem with working only with interfaces and maps is that you have to check all types. Struct ummarshalling handles that for you. – erik258 Oct 05 '21 at 17:01

2 Answers2

3

You can define the accurate nested structure corresponding to the JSON bytes, to Unmarshal.

package main

import (
    "encoding/json"
    "fmt"
)

type Trunk struct {
    Persons []Person `json:"persons"`
}

type Person struct {
    Name    string `json:"name"`
    Surname string `json:"surname"`
    Data    []User `json:"data"`
}

type User struct {
    Userid          string `json:"userid"`
    Username        string `json:"username"`
    Registered_date string `json:"registered_date"`
}

func main() {
    rawstring := []byte(`
    {
       "persons":[
          {
             "name":"mark",
             "surname":"zuckerberg",
             "data":[
                {
                   "userid":"09210",
                   "username":"mark290",
                   "registered_date":"10-01-2017"
                },

                {
                   "userid":"092240",
                   "username":"mark291",
                   "registered_date":"11-01-2017"
                }
             ]
          },

          {
             "name":"bill",
             "surname":"clinton",
             "data":[
                {
                   "userid":"0442340",
                   "username":"billy",
                   "registered_date":"10-01-2000"
                },

                {
                   "userid":"89472894",
                   "username":"billary",
                   "registered_date":"11-01-2015"
                }
             ]
          }
       ]
    }
    `)

    jsondata := Trunk{}
    if err := json.Unmarshal(rawstring, &jsondata); err == nil {
        fmt.Println(jsondata)

        // print first username of first person
        // fmt.Println(jsondata.Persons[0].Data[0].Username)
        // print each username of each person
        for _, person := range jsondata.Persons {
            for _, d := range person.Data {
                fmt.Println(d.Username)
            }
        }
    } else {
        fmt.Println("Unmarshal failed!")
    }
}

Or use a more compact structure defination:

type Trunk struct {
    Persons []struct {
        Name    string `json:"name"`
        Surname string `json:"surname"`
        Data    []struct {
            Userid          string `json:"userid"`
            Username        string `json:"username"`
            Registered_date string `json:"registered_date"`
        } `json:"data"`
    } `json:"persons"`
}
rustyhu
  • 1,912
  • 19
  • 28
1

If you want to use streaming approach you can try to Decoder to find "username" Token and get next Token with "value".

dec := json.NewDecoder(strings.NewReader(rawstring))
for {
    t, err := dec.Token()
    if err == io.EOF {
        break
    }
    if err != nil {
        log.Fatal(err)
    }
    if val, ok := t.(string); ok && val == "username" {
        t, err := dec.Token()
        if err == io.EOF {
            break
        }
        fmt.Println(t)
    }
}

PLAYGROUND

kozmo
  • 4,024
  • 3
  • 30
  • 48