在golang中,将http表单数据加载到struct中的一般函数

在Go中,http表单数据(例如来自POST或PUT请求)可以作为表单映射[string] []字符串的映射来访问.我很难以一种普遍的方式将其转换为结构体.

例如,我想加载一个地图,如:

m := map[string][]string {
    "Age": []string{"20"},"Name": []string{"John Smith"},}

进入如下模型:

type Person struct {
    Age   int
    Name string
}

所以我正在尝试使用签名LoadModel(obj interface {},m map [string] [] string)[]错误编写一个函数,它将表单数据加载到我可以键入的接口{}中.人.使用反射,以便我可以在任何字段上使用它,而不仅仅是一个Person,这样我就可以根据需要将字符串从http数据转换为int,boolean等.

使用这个问题的答案in golang,using reflect,how do you set the value of a struct field?我可以使用反射来设定一个人的价值,例如:

p := Person{25,"John"}
reflect.ValueOf(&p).Elem().Field(1).SetString("Dave")

但是我必须为我拥有的每种类型的结构复制加载函数.当我尝试接口{}时它不起作用.

pi := (interface{})(p)
reflect.ValueOf(&pi).Elem().Field(1).SetString("Dave")
// panic: reflect: call of reflect.Value.Field on interface Value

在一般情况下我该怎么做?或者甚至更好,是否有更惯用的Go方式来完成我想要做的事情?

为了好玩,我试了一下.请注意,我作弊了一点(见评论),但你应该得到照片.使用反射与静态类型分配(如nemo的答案)通常需要花费成本,所以一定要在你的决定中权衡(尽管我没有对它进行基准测试).

此外,明显的免责声明,我没有测试所有边缘情况等,不要只是复制粘贴在生产代码:)

所以这里是:

package main

import (
    "fmt"
    "reflect"
    "strconv"
)

type Person struct {
    Age    int
    Name   string
    Salary float64
}

// I cheated a little bit,made the map's value a string instead of a slice.
// Could've used just the index 0 instead,or fill an array of structs (obj).
// Either way,this shows the reflection steps.
//
// Note: no error returned from this example,I just log to stdout. Could definitely
// return an array of errors,and should catch a panic since this is possible
// with the reflect package.
func LoadModel(obj interface{},m map[string]string) {
    defer func() {
        if e := recover(); e != nil {
            fmt.Printf("Panic! %v\n",e)
        }
    }()

    val := reflect.ValueOf(obj)
    if val.Kind() == reflect.Ptr {
        val = val.Elem()
    }

    // Loop over map,try to match the key to a field
    for k,v := range m {
        if f := val.FieldByName(k); f.IsValid() {
            // Is it assignable?
            if f.CanSet() {

                // Assign the map's value to this field,converting to the right data type.
                switch f.Type().Kind() {
                // Only a few kinds,just to show the basic idea...
                case reflect.Int:
                    if i,e := strconv.ParseInt(v,0); e == nil {
                        f.SetInt(i)
                    } else {
                        fmt.Printf("Could not set int value of %s: %s\n",k,e)
                    }
                case reflect.Float64:
                    if fl,e := strconv.ParseFloat(v,0); e == nil {
                        f.SetFloat(fl)
                    } else {
                        fmt.Printf("Could not set float64 value of %s: %s\n",e)
                    }
                case reflect.String:
                    f.SetString(v)

                default:
                    fmt.Printf("Unsupported format %v for field %s\n",f.Type().Kind(),k)
                }
            } else {
                fmt.Printf("Key '%s' cannot be set\n",k)
            }
        } else {
            // Key does not map to a field in obj
            fmt.Printf("Key '%s' does not have a corresponding field in obj %+v\n",obj)
        }
    }
}

func main() {
    m := map[string]string{
        "Age":     "36","Name":    "Johnny","Salary":  "1400.33","Ignored": "True",}
    p := new(Person)
    LoadModel(p,m)
    fmt.Printf("After LoadModel: Person=%+v\n",p)
}

相关文章

什么是Go的接口? 接口可以说是一种类型,可以粗略的理解为他...
1、Golang指针 在介绍Golang指针隐式间接引用前,先简单说下...
1、概述 1.1 Protocol buffers定义 Protocol buffe...
判断文件是否存在,需要用到"os"包中的两个函数: os.Stat(...
1、编译环境 OS :Loongnix-Server Linux release 8.3 CPU指...
1、概述 Golang是一种强类型语言,虽然在代码中经常看到i:=1...