简体   繁体   English

如何将 JSON 解组为使用反射创建的值?

[英]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
}

I am trying to pass a type and a request into a function, then inside that function unMarshall the request into a variable and return it.我试图将一个类型和一个请求传递给一个函数,然后在该函数内部将请求解组为一个变量并返回它。

I assume my approach is wrong because when i try to do this:我认为我的方法是错误的,因为当我尝试这样做时:

inter, err := GetTypeFromReq(&c, models.User{})
if err != nil {
    revel.ERROR.Println(err.Error())
}
user := inter.(models.User)

I get the error "interface conversion: interface {} is reflect.Value, not models.User"我收到错误“接口转换:接口 {} 是 reflect.Value,而不是 models.User”

any tips on how to approach this?关于如何解决这个问题的任何提示?

Here's how to modify the the function to make it work as expected:以下是如何修改函数以使其按预期工作:

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
}

Note the calls to Interface() to get a reflect.Value 's current value.注意对Interface()的调用以获取reflect.Value的当前值。

Here's an approach that avoids reflection and type assertions:这是一种避免反射和类型断言的方法:

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
}

Use it like this:像这样使用它:

var user models.User
err := GetFromReq(&c, &user)
if err != nil {
   revel.ERROR.Println(err.Error())
}

Use a JSON decoder to simplify the code:使用JSON 解码器来简化代码:

func GetFromReq(c *App, item interface{}) error {
  defer c.Request.Body.Close()
  return json.NewDecoder(io.LimitReader(c.Request.Body, 1048576)).Deocode(item)
}

If c.Request is a *http.Request and c.Response is an http.ResponseWriter , then write the function as:如果c.Request*http.Request并且c.Responsehttp.ResponseWriter ,则将函数编写为:

func GetFromReq(c *App, item interface{}) error {
  return json.NewDecoder(http.MaxBytesReaer(c.Response, c.Request.Body, 1048576)).Deocode(item)
}

There's no need to close the request body in the net/http server.无需关闭 net/http 服务器中的请求正文。 Use MaxBytesReader instead of io.LimitReader to prevents clients from accidentally or maliciously sending a large request and wasting server resources.使用MaxBytesReader而不是 io.LimitReader 可以防止客户端意外或恶意发送大请求并浪费服务器资源。

修改最后一行代码:将user := inter.(models.User)改为user := inter.Interface().(models.User) ,试试看!

"interface conversion: interface {} is reflect.Value, not models.User" “接口转换:接口{}是reflect.Value,而不是models.User”

pretty straight forward about the message error.关于消息错误非常直截了当。 That your item is reflect.Value it is not models.User .你的itemreflect.Value它不是models.User

so I think in your code you can change the item to models.User .所以我认为在您的代码中您可以将item更改to models.User

But I assume that your are tying to create the function that will work with all type of your models, in this case models.User{} .但我假设您正在尝试创建适用于所有类型模型的函数,在本例中models.User{}

Your approach is expensive since it is using interface .您的方法很昂贵,因为它使用的是interface you could convert the incoming request directly like this:您可以像这样直接转换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
}

if your body has the same structure as your model it will give you the value, if not then it is error .如果您的body与您的模型具有相同的结构,它将为您提供价值,如果不是,则为error

Note that you need to be careful when using interface .请注意,使用interface时需要小心。 you can see some guideline in this article .您可以在 本文中看到一些指南。 Use an interface:使用接口:

  • When users of the API need to provide an implementation detail.当 API 的用户需要提供实现细节时。
  • When API's have multiple implementations they need to maintain internally.当 API 有多个实现时,它们需要在内部维护。
  • When parts of the API that can change have been identified and require decoupling.当 API 中可以更改的部分已被识别并需要解耦时。

Your function convert the value of your models.User to interface , and then return the interface value.您的函数将models.User的值转换为interface ,然后返回interface值。 that's why it's expensive.这就是为什么它很贵。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM