Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save skeptycal/598c0c4e07c8e3e3962b44ca8d8e8310 to your computer and use it in GitHub Desktop.
Save skeptycal/598c0c4e07c8e3e3962b44ca8d8e8310 to your computer and use it in GitHub Desktop.
Golang reflection; assign to struct field by tag name
package main
import (
"fmt"
"reflect"
"strings"
)
// The goal: allow assignment to a go struct field based on the name that field
// is tagged with (specifically it's json-tagged name).
type Wham struct {
Username string `json:"username,omitempty"`
Password string `json:"password"`
ID int64 `json:"_id"`
Homebase string `json:"homebase"`
}
func main() {
w := Wham{
Username: "maria",
Password: "hunter2",
ID: 42,
Homebase: "2434 Main St",
}
fmt.Printf("%+v\n", w)
SetField(&w, "username", "larry")
fmt.Printf("%+v\n", w)
}
func SetField(item interface{}, fieldName string, value interface{}) error {
v := reflect.ValueOf(item).Elem()
if !v.CanAddr() {
return fmt.Errorf("cannot assign to the item passed, item must be a pointer in order to assign")
}
// It's possible we can cache this, which is why precompute all these ahead of time.
findJsonName := func(t reflect.StructTag) (string, error) {
if jt, ok := t.Lookup("json"); ok {
return strings.Split(jt, ",")[0], nil
}
return "", fmt.Errorf("tag provided does not define a json tag", fieldName)
}
fieldNames := map[string]int{}
for i := 0; i < v.NumField(); i++ {
typeField := v.Type().Field(i)
tag := typeField.Tag
jname, _ := findJsonName(tag)
fieldNames[jname] = i
}
fieldNum, ok := fieldNames[fieldName]
if !ok {
return fmt.Errorf("field %s does not exist within the provided item")
}
fieldVal := v.Field(fieldNum)
fieldVal.Set(reflect.ValueOf(value))
return nil
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment