0

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?

qin ray
  • 3
  • 1
  • 1
    You properly used `Elem()` in your first example but not in the 2nd. Without calling `Elem()`, you only have a pointer value and not a struct value. – icza Dec 31 '19 at 14:11
  • So I need a addressable value <- Values := reflect.ValueOf(obj).Elem() <- obj must be a pointer <- pass a pointer to the obj param like "SetStringField2(&student)" – qin ray Jan 03 '20 at 05:23

2 Answers2

2

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.

0

Solution 1:

enter image description here

package service

import (
    "fmt"
    "reflect"
    "testing"
)


func SetStringField2(obj interface{}) {
    Values := reflect.ValueOf(obj).Elem()
    count := reflect.Indirect(reflect.ValueOf(obj)).NumField()
    for i := 0; i < count; i++ {
        fieldValue := Values.Field(i)
        switch fieldValue.Kind() {
        case reflect.String:
            fieldValue.SetString("fieldCleanString2 set name")
        }
    }
}

func TestSetValue(t *testing.T) {

    type Student struct {
        Name string
        Age  int
    }
    student := &Student{
        "alice",
        12,
    }

    SetStringField2(student)

    fmt.Print(student.Name)
}
Donutloop
  • 427
  • 3
  • 15