如何从字符串表示形式的变量中获取嵌套结构?

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{})