I have a JSON file looks like following somehow
{
"type": "Weekly",
"clients": [
"gozo",
"dva"
],
"sender": "no-reply@flowace.in",
"recipients": {
"gozo": [
"a@gmail.com",
"b@hotmail.com"
],
"dva": [
"c@gmail.com",
"d@hotmail.com"
]
},
"features": [
"Top5UsedApps",
"TimeSpentOnEachL3",
"NewlyAssignedL3",
"HoursLogged"
],
"dbCloning": [
"dva"
]
}
I have mapped structs like the following.
type receivers struct {
Gozo []string `json:"gozo"`
Dva []string `json:"dva"`
// Add more recievers
}
// Config -- The config object parsed from the JSON file
type Config struct {
ReportType string `json:"type"`
Clients []string `json:"clients"`
Sender string `json:"sender"`
Recipients receivers `json:"recipients"`
Cloning []string `json:"dbCloning"`
}
Then somewhere in the another source file, I do the following,
func main() {
conf := LoadConfig(os.Args[1])
for _, client := range conf.Clients {
// Use the client variable of some other function calls
fmt.Println(conf.Recipients[client]) // This will not work
}
Now my question is how can I make it work. I can not loop over conf.Recipients
directly.
PS: Consider that LoadConfig
function unmarshal the JSON and returns a conf
object.
Edit 1: It looks like it was design decision error. Now going with the solution of map[string][]string
. But not marking it as an answer because the need to know how it is easily done for all the cases where there was no other choices.
The problem is that your type receivers
shouldn't have named fields. It should be a map[string][]string
instead.
Here is a working example:
package main
import (
"encoding/json"
"fmt"
)
type Config struct {
ReportType string `json:"type"`
Clients []string `json:"clients"`
Sender string `json:"sender"`
Recipients map[string][]string `json:"recipients"`
Cloning []string `json:"dbCloning"`
}
var data = []byte(`{
"type": "Weekly",
"clients": [
"gozo",
"dva"
],
"sender": "no-reply@flowace.in",
"recipients": {
"gozo": [
"a@gmail.com",
"b@hotmail.com"
],
"dva": [
"c@gmail.com",
"d@hotmail.com"
]
},
"features": [
"Top5UsedApps",
"TimeSpentOnEachL3",
"NewlyAssignedL3",
"HoursLogged"
],
"dbCloning": [
"dva"
]
}`)
func main() {
var conf Config
json.Unmarshal(data, &conf)
for _, client := range conf.Clients {
fmt.Println(conf.Recipients[client])
}
}
Gives the output
[a@gmail.com b@hotmail.com]
[c@gmail.com d@hotmail.com]
A range clause provides a way to iterate over an array, slice, string, map, or channel. So you cannot range over struct until you convert it into one of the above types.
To loop over struct fields you can go for reflection by creating a slice of reflect value for struct.
package main
import (
"encoding/json"
"fmt"
"reflect"
)
type receivers struct {
Gozo []string `json:"gozo"`
Dva []string `json:"dva"`
// Add more recievers
}
// Config -- The config object parsed from the JSON file
type Config struct {
ReportType string `json:"type"`
Clients []string `json:"clients"`
Sender string `json:"sender"`
Recipients receivers `json:"recipients"`
Cloning []string `json:"dbCloning"`
}
var data = []byte(`{
"type": "Weekly",
"clients": [
"gozo",
"dva"
],
"sender": "no-reply@flowace.in",
"recipients": {
"gozo": [
"a@gmail.com",
"b@hotmail.com"
],
"dva": [
"c@gmail.com",
"d@hotmail.com"
]
},
"features": [
"Top5UsedApps",
"TimeSpentOnEachL3",
"NewlyAssignedL3",
"HoursLogged"
],
"dbCloning": [
"dva"
]
}`)
func main() {
var conf Config
if err := json.Unmarshal(data, &conf); err != nil{
fmt.Println(err)
}
v := reflect.ValueOf(conf.Recipients)
values := make([]interface{}, v.NumField())
for i := 0; i < v.NumField(); i++ {
values[i] = v.Field(i).Interface()
}
fmt.Println(values)
}
Working Code on Go Playground
NumField
returns the number of fields in the struct v. It panics if v's Kind is not Struct.
func (v Value) NumField() int
Field
returns the i'th field of the struct v. It panics if v's Kind is not Struct or i is out of range.
func (v Value) Field(i int) Value
Interface returns v's current value as an interface{}.
func (v Value) Interface() (i interface{})