Go递归解析表单数据到嵌套结构体遇字段覆盖问题求助
问题:表单数据递归解析为嵌套结构体时仅能设置一个内部字段
尝试将表单数据字典递归解析为嵌套结构体,当前功能仅部分可用,最终内部结构体仅能设置一个字段,推测是旧结构体被不断覆盖导致,寻求解决方案。
函数入口代码
func FormDataToStruct(data map[string]string, s any) { // Parse the form data into the struct v := reflect.ValueOf(s) if v.Kind() == reflect.Ptr { v = v.Elem() } if v.Kind() != reflect.Struct { panic("not a struct") } formParse(data, v, s) }
顶层结构体赋值逻辑
func formParse(data map[string]string, v reflect.Value, s any) { for key, value := range data { if hasField(v, key) { println("Found field: " + key) var val, err = TransformValue(s, key, value) if err != nil { panic(err) } SetValue(s, key, val) delete(data, key) } } // Set values of inner structs for key, value := range data { keys := strings.Split(key, "_") // Parse the inner struct recursively with another function recurseKeys(keys, value, v, s, s) } }
结构体定义
type Me struct { Name string Age int Friend struct { Name string Age int } }
待解析的表单数据
map[name:John age:20 friend_name:Jane friend_age:20]
递归处理函数
func recurseKeys(keys []string, value string, v reflect.Value, s any, parent any) { if len(keys) == 1 { // We are at the end of the keys // Set the value var val, err = TransformValue(s, keys[0], value) if err != nil { panic(err) } SetValue(s, keys[0], val) return } // We are not at the end of the keys // We need to iterate over the struct for i := 0; i < v.NumField(); i++ { if strings.EqualFold(v.Type().Field(i).Name, keys[0]) { // We found the field // Recurse with the next key newS := reflect.New(v.Field(i).Type()) recurseKeys(keys[1:], value, v.Field(i), newS.Interface(), parent) // Check if the field on the old struct is a pointer, if it is, we need to set the pointer // If it is not a pointer, we need to set the value if v.Field(i).Kind() == reflect.Ptr { // Check if newS is a pointer if newS.Kind() == reflect.Ptr { v.Field(i).Set(newS) } else { v.Field(i).Set(newS.Elem()) } } else { // Check if newS is a pointer if newS.Kind() == reflect.Ptr { v.Field(i).Set(newS.Elem()) } else { v.Field(i).Set(newS) } } } } }
运行结果
println(meInstance) // {John 20 {Jane 0}}
问题根源与解决方案
问题根源
recurseKeys函数中,每次处理内部字段时都会创建全新的结构体实例(newS := reflect.New(v.Field(i).Type())),并将这个新实例赋值给原结构体的对应字段。当处理friend_age时,会重新创建一个只设置了Age字段的Friend结构体,覆盖掉之前已设置Name的旧结构体,最终导致内部结构体仅最后处理的字段生效。
修正后的递归处理函数
直接操作原结构体中已存在的内部结构体字段,无需每次创建新实例:
func recurseKeys(keys []string, value string, v reflect.Value, parent any) { if len(keys) == 1 { // 到达最后一级字段,直接设置值 val, err := TransformValue(parent, keys[0], value) if err != nil { panic(err) } SetValue(parent, keys[0], val) return } // 遍历结构体字段,找到匹配的层级字段 for i := 0; i < v.NumField(); i++ { field := v.Field(i) fieldType := v.Type().Field(i) if strings.EqualFold(fieldType.Name, keys[0]) { // 确保字段可设置 if !field.CanSet() { panic("cannot set field " + fieldType.Name) } var nextV reflect.Value if field.Kind() == reflect.Ptr { // 指针为空时先创建实例 if field.IsNil() { field.Set(reflect.New(field.Type().Elem())) } nextV = field.Elem() } else if field.Kind() == reflect.Struct { nextV = field } else { // 非结构体类型跳过 continue } // 递归处理剩余键 recurseKeys(keys[1:], value, nextV, parent) break } } }
修正formParse函数的调用逻辑
去掉多余参数,传递当前层级反射值和父结构体:
func formParse(data map[string]string, v reflect.Value, s any) { // 处理顶层字段 for key, value := range data { if hasField(v, key) { println("Found field: " + key) val, err := TransformValue(s, key, value) if err != nil { panic(err) } SetValue(s, key, val) delete(data, key) } } // 处理嵌套结构体字段 for key, value := range data { keys := strings.Split(key, "_") recurseKeys(keys, value, v, s) } }
修正后的运行结果
{John 20 {Jane 20}}
内容的提问来源于stack exchange,提问作者nigel239
相关产品推荐
相关产品推荐

