英文:
Assigning a value to struct member through reflection in Go
问题
我有一个结构体v,其中包含成员A、B、C字符串。使用反射,我可以获取字段的名称和它们的值:
typ := v.Type()
for i := 0; i < v.NumField(); i++ {
// 获取StructField
fi := typ.Field(i)
fieldname := fi.Name
fmt.Println(fieldname)
val := fmt.Sprintf("%v", v.Field(i).Interface())
}
由于我有字段的名称,并且可以获取值,那么我能否给字段赋新值呢?我想要做的是:
v.Field(fieldname).Interface() = "new value"
但显然这是行不通的。如果只知道字段的名称,是否可以将值赋给结构体的字段呢?
实际上,我试图将map[string]string
中的值分配给结构体中对应的字段,其中结构体和映射的定义可能随时间扩展或更改,并且映射中的值可能比结构体多或少。我考虑过使用JSON来实现,但这种方法让我有些不满意,因为使用反射几乎可以实现这一目标!
谢谢,
Ken
英文:
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
答案1
得分: 1
是的,这是可能的。
介绍
由于您想要访问和修改变量(或字段)的值,您需要使用reflect.Value
类型,而不是reflect.Type
。您可以使用reflect.ValueOf()
获取它。此外,为了使用反射修改它,您需要传递要修改的struct
或值的地址(指针)(否则您只能读取它而不能修改它)。
但是您不想修改地址/指针,而是修改指向的值,因此您需要从指针的Value
导航到指向的变量(struct)的Value
,这就是Value.Elem()
的作用。它的用法如下:reflect.ValueOf(&s).Elem()
您可以使用Value.FieldByName()
方法获取结构字段的Value
,由于我们将指针的地址传递给了ValueOf()
函数,因此它将是可设置的。
代码
一旦理解了介绍,代码就比较简单。您还可以在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)
输出:
Before: {a1 b2 c3}
After: {2a 2b 2c}
From Map: {ma mb mc}
我建议阅读这篇博文,以了解Go中反射的基础知识:
英文:
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:
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论