英文:
recursively iterating struct and set fields with go using reflect
问题
我想构建一个使用反射设置结构体字段的程序。我已经成功实现了顶层字段的设置,但是在处理嵌套结构体字段时遇到了困难。如何迭代嵌套结构体字段?
type Payload struct {
Type string `json:"type"`
SubItem *SubItem `json:"sub_item"`
}
type SubItem struct {
Foo string `json:"foo"`
}
func main() {
var payload Payload
setValue(&payload, "type", "test1")
setValue(&payload, "sub_item.foo", "test2")
}
func setValue(structPtr interface{}, key string, value string) {
structValue := reflect.Indirect(reflect.ValueOf(structPtr))
for i, subkey := range strings.Split(key, ".") {
isLast := i == len(strings.Split(key, "."))-1
var found bool
// 这一行会报错:"reflect: call of reflect.Value.NumField on zero Value"
for i := 0; i < structValue.NumField(); i++ {
field := structValue.Type().Field(i)
jsonTags := strings.Split(field.Tag.Get("json"), ",")
if jsonTags[0] == subkey {
found = true
if isLast {
if isLast {
// 最后一个元素
// TODO 设置值
fmt.Printf("TODO 设置值 %s 到 %v", value, structValue)
structValue = reflect.Indirect(reflect.ValueOf(structPtr))
}
} else {
structValue = reflect.Indirect(reflect.ValueOf(structValue.Field(i).Interface()))
}
break
}
}
if !found {
panic(fmt.Errorf("未找到字段 %s", key))
}
}
}
以上是你提供的代码的翻译。
英文:
I want to build the program that sets field of struct using reflection. I made it work for top-level field but I am struggling with nested struct field. How can iterate over nested struct field?
type Payload struct {
Type string `json:"type"`
SubItem *SubItem `json:"sub_item"`
}
type SubItem struct {
Foo string `json:"foo"`
}
func main() {
var payload Payload
setValue(&payload, "type", "test1")
setValue(&payload, "sub_item.foo", "test2")
}
func setValue(structPtr interface{}, key string, value string) {
structValue := reflect.Indirect(reflect.ValueOf(structPtr))
for i, subkey := range strings.Split(key, ".") {
isLast := i == len(strings.Split(key, "."))-1
var found bool
// this line is crashing with "reflect: call of reflect.Value.NumField on zero Value"
for i := 0; i < structValue.NumField(); i++ {
field := structValue.Type().Field(i)
jsonTags := strings.Split(field.Tag.Get("json"), ",")
if jsonTags[0] == subkey {
found = true
if isLast {
if isLast {
// last element
// TODO set value
fmt.Printf("TODO set value %s to %v", value, structValue)
structValue = reflect.Indirect(reflect.ValueOf(structPtr))
}
} else {
structValue = reflect.Indirect(reflect.ValueOf(structValue.Field(i).Interface()))
}
break
}
}
if !found {
panic(fmt.Errorf("failed to find field %s", key))
}
}
}
答案1
得分: 4
请使用以下函数:
func setValue(p interface{}, key string, value interface{}) {
v := reflect.ValueOf(p)
// 通过 key 中的名称循环查找目标字段。
for _, name := range strings.Split(key, ".") {
// 如果值是指针,则
// - 如果 ptr 为 nil,则分配值。
// - 解引用 ptr
for v.Kind() == reflect.Ptr {
if v.IsNil() {
v.Set(reflect.New(v.Type().Elem()))
}
v = v.Elem()
}
// 我们期望值是结构体。查找具名字段。
v = findJSONField(v, name)
if !v.IsValid() {
panic(fmt.Sprintf("无法找到字段 %s", key))
}
}
// 设置字段的值。
v.Set(reflect.ValueOf(value))
}
// 函数 findJSONField 通过字段的 JSON 标签查找结构体字段。
func findJSONField(v reflect.Value, name string) reflect.Value {
t := v.Type()
for i := 0; i < v.NumField(); i++ {
if tag, _, _ := strings.Cut(t.Field(i).Tag.Get("json"), ","); tag == name {
return v.Field(i)
}
}
return reflect.Value{}
}
你可以在这里查看代码:https://go.dev/play/p/DnSuydQgQt9
英文:
Use this function:
func setValue(p interface{}, key string, value interface{}) {
v := reflect.ValueOf(p)
// Loop through the names in key to find the target field.
for _, name := range strings.Split(key, ".") {
// If the value is pointer, then
// - allocate value if ptr is nil.
// - indirect ptr
for v.Kind() == reflect.Ptr {
if v.IsNil() {
v.Set(reflect.New(v.Type().Elem()))
}
v = v.Elem()
}
// We expect that the value is struct. Find the
// named field.
v = findJSONField(v, name)
if !v.IsValid() {
panic(fmt.Sprintf("could not find field %s", key))
}
}
// Set the field.
v.Set(reflect.ValueOf(value))
}
The function findJSONField finds a struct field by the field's JSON tag:
func findJSONField(v reflect.Value, name string) reflect.Value {
t := v.Type()
for i := 0; i < v.NumField(); i++ {
if tag, _, _ := strings.Cut(t.Field(i).Tag.Get("json"), ","); tag == name {
return v.Field(i)
}
}
return reflect.Value{}
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论