You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.09 04:25:21