Charles Bryant
Charles Bryant

Reputation: 1015

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?

Upvotes: 14

Views: 13896

Answers (2)

Arman Ordookhani
Arman Ordookhani

Reputation: 6537

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 (e.g. you could range over it, check membership and ... in one line).

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

Upvotes: 1

Thundercat
Thundercat

Reputation: 120931

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

Upvotes: 17

Related Questions