Go语言 从dict键递归分析结构

l2osamch  于 2022-12-16  发布在  Go
关注(0)|答案(1)|浏览(108)

我正在尝试递归解析表单数据字典到一个嵌入式结构中。它是 kindof 工作的,但最终只有一个字段得到设置。我认为该字段一直被旧结构覆盖,但需要一些帮助来找到解决方案。
函数的入口点:

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
        }
    }

要分析的表单数据如下所示:
一个三个三个一个
通过该结构运行上述表单数据将产生以下输出:

println(meInstance)
// {John 20 {Jane 0}}

任何帮助都是非常感谢!

  • 编辑 *

使用最少的可重复示例进行Playground游戏:https://go.dev/play/p/d5pIK3uQrUL

2ledvvac

2ledvvac1#

这段代码的问题在于它创建了一个新的struct并用现有的对象替换它。因此,你总是会看到最后一次赋值。
代码的修复版本如下所示:

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
            if v.Field(i).IsZero() {
                var newS reflect.Value
                if v.Field(i).Kind() == reflect.Ptr {
                    newS = reflect.New(v.Field(i).Type().Elem()).Elem()
                } else {
                    newS = reflect.New(v.Field(i).Type())
                }

                // 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 {
                    v.Field(i).Set(newS.Addr())
                } else {
                    v.Field(i).Set(newS.Elem())
                }

            }
            if v.Field(i).Kind() == reflect.Ptr {
                recurseKeys(keys[1:], value, v.Field(i), v.Field(i).Interface(), parent)
            } else {
                recurseKeys(keys[1:], value, v.Field(i), v.Field(i).Addr().Interface(), parent)
            }

        }
    }
}

这段代码也接受结构指针。
性能改进提示:您可能需要考虑扫描目标对象并创建name -> Reflect value的贴图以减少循环数。
保养提示:最好考虑使用struct tags,而不是直接反映结构变量名。

相关问题