2

I have a struct v with members A, B, C string. Using reflection, I can get the names of the fields and their values:

typ := v.Type()
for i := 0; i < v.NumField(); i++ {
    // gets us a StructField
    fi := typ.Field(i)
    fieldname := fi.Name
    fmt.Println(fieldname)
    val := fmt.Sprintf("%v", v.Field(i).Interface())
 }

since I have the name, and can get the value OUT, can I assign new values to the fields? I would like to do essentially:

v.Field(fieldname).Interface() = "new value"

but that obviously doesn't work. Is it possible to assign a value into a struct if you only know the name of the field?

In practice, I'm trying to assign values from a map[string]string to corresponding fields in a struct, where the struct and map definitions may expand of change over time, and the map may contain more, or less, values than the struct. I've considered doing it w/JSON, but that approach leaves me a little cold, seeing as how easy it was to use reflection to get "almost" there!

Thanks, Ken

icza
  • 389,944
  • 63
  • 907
  • 827
Ken
  • 369
  • 3
  • 15

1 Answers1

1

Yes, it is possible.

Introduction

Since you want to access and modify the value of a variable (or field), you need to use the reflect.Value type instead of reflect.Type. You can acquire it with reflect.ValueOf(). Also in order to modify it with reflection, you need to pass the address (a pointer) of the struct or value you want to modify (else you could only read it but not modify it).

But you don't want to modify the address/pointer but the pointed value, so you have to "navigate" from the Value of the pointer to the Value of the pointed variable (struct), this is what Value.Elem() is for. It looks like this: reflect.ValueOf(&s).Elem()

You can get the Value of a struct field with the Value.FieldByName() method, which since we passed the address of the pointer to the ValueOf() function will be settable.

The Code

The code is much simpler than the introduction once you understand it. You can also try it on the Go Playground:

var s struct {
    A, B, C string
}
s.A, s.B, s.C = "a1", "b2", "c3"

fmt.Println("Before:  ", s)

v := reflect.ValueOf(&s).Elem()

v.FieldByName("A").SetString("2a")
v.FieldByName("B").SetString("2b")
v.FieldByName("C").SetString("2c")

fmt.Println("After:   ", s)

// Using a map:
m := map[string]string{"A": "ma", "B": "mb", "C": "mc"}
for mk, mv := range m {
    v.FieldByName(mk).SetString(mv)
}

fmt.Println("From Map:", s)

Output:

Before:   {a1 b2 c3}
After:    {2a 2b 2c}
From Map: {ma mb mc}

I recommend to read this blog post to learn the basics of the reflection in Go:

The Laws of Reflection

icza
  • 389,944
  • 63
  • 907
  • 827