Skip to content

Instantly share code, notes, and snippets.

@lelandbatey
Last active November 8, 2023 15:20
Show Gist options
  • Star 14 You must be signed in to star a gist
  • Fork 3 You must be signed in to fork a gist
  • Save lelandbatey/a5c957b537bed39d1d6fb202c3b8de06 to your computer and use it in GitHub Desktop.
Save lelandbatey/a5c957b537bed39d1d6fb202c3b8de06 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", fieldName)
}
fieldVal := v.Field(fieldNum)
fieldVal.Set(reflect.ValueOf(value))
return nil
}
@clementlecorre
Copy link

I think there is an error here, a value is missing

return fmt.Errorf("field %s does not exist within the provided item")
👍
return fmt.Errorf("field %s does not exist within the provided item", fieldName)

@lelandbatey
Copy link
Author

@clementlecorre You're totally correct, thank you for the fix!

@dolmen
Copy link

dolmen commented Mar 10, 2022

Fix: skip field if jname == "" || jname == "-"

@santoselmer1
Copy link

Nicw

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment