Search code examples
goreflect

How to update all string fields of arbitrary go struct?


I try to write a function to update all string fields of arbitrary structs, like this:

type Student struct {
  Name  string
  Age   int
}

func SetStringField(obj interface{}) {
    reflect.ValueOf(obj).Elem().FieldByName("Name").SetString("set name")
}

func main() {
  student := Student{
    "alice",
    12,
  }

  SetStringField(&student)
}

but this func can only update specific field. And I try this:


func SetStringField2(obj interface{}) {
    // Keys := reflect.TypeOf(obj)
    Values := reflect.ValueOf(obj)
    count := reflect.ValueOf(obj).NumField()
    for i := 0; i < count; i++ {
        // fieldKey := Keys.Field(i).Name
        fieldValue := Values.Field(i)
        switch fieldValue.Kind() {
        case reflect.String:
            // fieldValue.CanSet()==false
            fieldValue.SetString("fieldCleanString2 set name")
            // panic: reflect: call of reflect.Value.FieldByName on interface Value
            // reflect.ValueOf(&obj).Elem().FieldByName(fieldKey).SetString("123")
        }
    }
}

func main() {
  student := Student{
    "alice",
    12,
  }
  SetStringField2(student)
}
  1. fieldValue.SetString() got "panic: reflect: reflect.flag.mustBeAssignable using unaddressable value" because fieldValue.CanSet()==false.
  2. reflect.ValueOf(&obj).Elem().FieldByName(fieldKey).SetString("fieldCleanString2 set name") also fail, got "panic: reflect: call of reflect.Value.FieldByName on interface Value".

And calling SetStringField2(&student) got "panic: reflect: call of reflect.Value.NumField on ptr Value"

So, are there any other methods to do this work?


Solution

  • The problem is that the reflect value is not settable. To fix this issue, create the reflect value from a pointer.

    // SetStringField2 sets strings fields on the struct pointed to by ps.
    func SetStringField2(ps interface{}) {
        v := reflect.ValueOf(ps).Elem() // Elem() dereferences pointer
        for i := 0; i < v.NumField(); i++ {
            fv := v.Field(i)
            switch fv.Kind() {
            case reflect.String:
                fv.SetString("fieldCleanString2 set name")
            }
        }
    }
    

    Pass a pointer to the value to the function:

    student := Student{
        "alice",
        12,
    }
    SetStringField2(&student)
    

    Run it on the playground.