简体   繁体   中英

Golang dynamic access to a struct property

I am writing a quick ssh config to json processor in golang. I have the following stuct:

type SshConfig struct {
    Host string
    Port string
    User string
    LocalForward string
    ...
}

I am currently looping over every line of my ssh config file and splitting the line on spaces and checking which property to update.

if split[0] == "Port" {
    sshConfig.Port = strings.Join(split[1:], " ")
}

Is there a way to check a property exists and then set it dynamically?

Use the reflect package to set a field by name:

// setField sets field of v with given name to given value.
func setField(v interface{}, name string, value string) error {
    // v must be a pointer to a struct
    rv := reflect.ValueOf(v)
    if rv.Kind() != reflect.Ptr || rv.Elem().Kind() != reflect.Struct {
        return errors.New("v must be pointer to struct")
    }

    // Dereference pointer
    rv = rv.Elem()

    // Lookup field by name
    fv := rv.FieldByName(name)
    if !fv.IsValid() {
        return fmt.Errorf("not a field name: %s", name)
    }

    // Field must be exported
    if !fv.CanSet() {
        return fmt.Errorf("cannot set field %s", name)
    }

    // We expect a string field
    if fv.Kind() != reflect.String {
        return fmt.Errorf("%s is not a string field", name)
    }

    // Set the value
    fv.SetString(value)
    return nil
}

Call it like this:

var config SshConfig

...

err := setField(&config, split[0], strings.Join(split[1:], " "))
if err != nil {
   // handle error
}

playground example

You could use reflect package to dynamically inspect structs at runtime. See docs and blog .

But if all you want to do is read the file into a structure its better to use something like map[string]string . Inspecting maps at runtime is much cleaner (eg you could range over it, check membership and ... in one line).

m := make(map[string]string)
...
m[split[0]] = strings.Join(split[1:], " ")

I ended up defining a method on my type and use an input to that method to return the attribute I wanted. Not ideal but it is straightforward and works well.

type PinMap struct {
    Relay101 map[string]int `json:"192.168.1.101"`
    Relay102 map[string]int `json:"192.168.1.102"`
}

// GetMap Allow dyanamic access of PinMap while maintaining struct
func (pm PinMap) GetMap(ip string) map[string]int {
    switch ip {
    case "192.168.1.101":
        return pm.Relay101
    case "192.168.1.102":
        return pm.Relay102
    default:
        return pm.Relay101 // should probably throw and error
    }
}

and then I used the code like this:

relayPinMap := pumps.pinMap.GetMap(relayID)

Pretty new to golang so please let me know if this is bad practice - will take down the answer if there is a good reason to.

Also note this felt hacky to me, but I needed it to work and I had to get it working in minutes😅. What I think would be better would be to make the type map[string]map[string]int , ie use a map instead of a struct when you need dynamic access if you can.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM