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