Last active
November 8, 2023 15:20
-
-
Save lelandbatey/a5c957b537bed39d1d6fb202c3b8de06 to your computer and use it in GitHub Desktop.
Golang reflection; assign to struct field by tag name
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 You're totally correct, thank you for the fix!
Fix: skip field if jname == "" || jname == "-"
Nicw
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
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)