Fill struct dynamically from parameters

Viewed 272

I've the following struct which works as expected Im getting data and I was able

type Service struct {
    Resources    []struct {
        Model struct {
            Name                string `json:"name"`
            Credentials         struct {
                path string `json:"path"`
                Vts struct {
                    user string `json:"user"`
                    id    string `json:"id"`
                    address      string `json:"address"`
                } `json:"vts"`
            } `json:"credentials"`
        } `json:"model"`
    } `json:"resources"`
}

service:= Service{}
err := json.Unmarshal([]byte(data), &service

The data is like following,

service1

{
    "resources": [
        "model": {
          "name": "cred",
          "credentials": {
            "path": "path in fs",
            "vts": {
              "user": "stephane", 
              "id": "123",
              "address": "some address"
            }
          },
        }
      },

Now some services providing additional data under vts for example now we have 3 fields (user/id/address) but some services (service1) can provides additional data like email, secondName ,etc . but the big problem here is that I need to get it from parameters since (service 2) education, salary etc

Service2

{
    "resources": [
        "model": {
          "name": "cred",
          "credentials": {
            "path": "path in fs",
            "vts": {
              "user": "stephane",
              "id": "123",
              "address": "some address",
              "email" : "some email",
              "secondName" : "secondName"
            }
          },
        }
      },

service N

{
    "resources": [
        "model": {
          "name": "cred",
          "credentials": {
            "path": "path in fs",
            "vts": {
              "user": "stephane",
              "id": "123",
              "address": "some address",
              "salary" : "1000000"
            }
          },
        }
      },

Of course If I know in advance the fields I can put them all in the struct and use omitempty but I dont know, I just get it as parameter to the function (the new properties names) , some service can provide 10 more fields in this struct (which I should get the properties name of them as args[]to the functions) but I don't know them in advance, this should be dynamic somehow ....is there a nice way to handle it in Golang ?

1 Answers

If you don't know the fields in advance, then don't use a struct but something that is also "dynamic": a map.

type Service struct {
    Resources []struct {
        Model struct {
            Name        string `json:"name"`
            Credentials struct {
                Path string                 `json:"path"`
                Vts  map[string]interface{} `json:"vts"`
            } `json:"credentials"`
        } `json:"model"`
    } `json:"resources"`
}

map[sting]interface{} can hold values of any type. If you know all fields will hold a string value, you may also use a map[string]string so it will be easier to work with it.

Example with input JSON:

{
    "resources": [
      {
        "model": {
          "name": "cred",
          "credentials": {
            "path": "path in fs",
            "vts": {
              "user": "stephane", 
              "id": "123",
              "address": "some address",
              "dyn1": "d1value",
              "dyn2": "d2value"
            }
          }
        }
      }
    ]
}

Testing it:

service := Service{}
err := json.Unmarshal([]byte(data), &service)
fmt.Printf("%q %v", service, err)

Output (try it on the Go Playground):

{[{{"cred" {"path in fs" map["dyn2":"d2value" "user":"stephane" "id":"123" 
    "address":"some address" "dyn1":"d1value"]}}}]} <nil>

Now if you want to collect values from the Vts map for a set of keys, this is how you can do it:

args := []string{"dyn1", "dyn2"}
values := make([]interface{}, len(args))
for i, arg := range args {
    values[i] = service.Resources[0].Model.Credentials.Vts[arg]
}
fmt.Println(values)

Output of the above will be (try it on the Go Playground):

[d1value d2value]
Related