[英]How to unmarshall JSON into a value created with reflection?
package controllers
import (
"encoding/json"
"errors"
"io"
"io/ioutil"
"reflect"
)
func GetTypeFromReq(c *App, ty interface{}) (interface{}, error) {
//get the type we are going to marshall into
item := reflect.ValueOf(ty)
//define and set the error that we will be returning to null
var retErr error
retErr = nil
//extract the body from the request and defer closing of the body
body, err := ioutil.ReadAll(io.LimitReader(c.Request.Body, 1048576))
defer c.Request.Body.Close()
//handle errors and unmarshal our data
if err != nil {
retErr = errors.New("Failed to Read body: " + err.Error())
} else if err = json.Unmarshal(body, &item); err != nil {
retErr = errors.New("Unmarshal Failed: " + err.Error())
}
return item, retErr
}
我试图将一个类型和一个请求传递给一个函数,然后在该函数内部将请求解组为一个变量并返回它。
我认为我的方法是错误的,因为当我尝试这样做时:
inter, err := GetTypeFromReq(&c, models.User{})
if err != nil {
revel.ERROR.Println(err.Error())
}
user := inter.(models.User)
我收到错误“接口转换:接口 {} 是 reflect.Value,而不是 models.User”
关于如何解决这个问题的任何提示?
以下是如何修改函数以使其按预期工作:
func GetTypeFromReq(c *App, ty interface{}) (interface{}, error) {
// Allocate new value with same type as ty
v := reflect.New(reflect.TypeOf(ty))
//define and set the error that we will be returning to null
var retErr error
retErr = nil
//extract the body from the request and defer closing of the body
body, err := ioutil.ReadAll(io.LimitReader(c.Request.Body, 1048576))
defer c.Request.Body.Close()
//handle errors and unmarshal our data
if err != nil {
retErr = errors.New("Failed to Read body: " + err.Error())
} else if err = json.Unmarshal(body, v.Interface()); err != nil {
retErr = errors.New("Unmarshal Failed: " + err.Error())
}
// v holds a pointer, call Elem() to get the value.
return v.Elem().Interface(), retErr
}
注意对Interface()的调用以获取reflect.Value
的当前值。
这是一种避免反射和类型断言的方法:
func GetFromReq(c *App, item interface{}) error {
//extract the body from the request and defer closing of the body
body, err := ioutil.ReadAll(io.LimitReader(c.Request.Body, 1048576))
defer c.Request.Body.Close()
//handle errors and unmarshal our data
if err != nil {
retErr = errors.New("Failed to Read body: " + err.Error())
} else if err = json.Unmarshal(body, item); err != nil {
retErr = errors.New("Unmarshal Failed: " + err.Error())
}
return retErr
}
像这样使用它:
var user models.User
err := GetFromReq(&c, &user)
if err != nil {
revel.ERROR.Println(err.Error())
}
使用JSON 解码器来简化代码:
func GetFromReq(c *App, item interface{}) error {
defer c.Request.Body.Close()
return json.NewDecoder(io.LimitReader(c.Request.Body, 1048576)).Deocode(item)
}
如果c.Request
是*http.Request
并且c.Response
是http.ResponseWriter
,则将函数编写为:
func GetFromReq(c *App, item interface{}) error {
return json.NewDecoder(http.MaxBytesReaer(c.Response, c.Request.Body, 1048576)).Deocode(item)
}
无需关闭 net/http 服务器中的请求正文。 使用MaxBytesReader而不是 io.LimitReader 可以防止客户端意外或恶意发送大请求并浪费服务器资源。
修改最后一行代码:将user := inter.(models.User)
改为user := inter.Interface().(models.User)
,试试看!
“接口转换:接口{}是reflect.Value,而不是models.User”
关于消息错误非常直截了当。 你的item
是reflect.Value
它不是models.User
。
所以我认为在您的代码中您可以将item
更改to models.User
。
但我假设您正在尝试创建适用于所有类型模型的函数,在本例中models.User{}
。
您的方法很昂贵,因为它使用的是interface
。 您可以像这样直接转换incoming request
:
func GetTypeFromReq(c *App, ty models.User) (models.User, error) {
//get the type we are going to marshall into
var item models.User
//define and set the error that we will be returning to nil
var retErr error // this var if the value not define then it is nil. Because error is interface
//extract the body from the request and defer closing of the body
body, err := ioutil.ReadAll(io.LimitReader(c.Request.Body, 1048576))
defer c.Request.Body.Close()
//handle errors and unmarshal our data
if err != nil {
retErr = errors.New("Failed to Read body: " + err.Error())
} else if err = json.Unmarshal(body, &item); err != nil {
retErr = errors.New("Unmarshal Failed: " + err.Error())
}
return item, retErr
}
如果您的body
与您的模型具有相同的结构,它将为您提供价值,如果不是,则为error
。
请注意,使用interface
时需要小心。 您可以在 本文中看到一些指南。 使用接口:
您的函数将models.User
的值转换为interface
,然后返回interface
值。 这就是为什么它很贵。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.