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

我試圖將一個類型和一個請求傳遞給一個函數,然后在該函數內部將請求解組為一個變量並返回它。

我認為我的方法是錯誤的,因為當我嘗試這樣做時:

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.Responsehttp.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”

關於消息錯誤非常直截了當。 你的itemreflect.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時需要小心。 您可以在 本文中看到一些指南。 使用接口:

  • 當 API 的用戶需要提供實現細節時。
  • 當 API 有多個實現時,它們需要在內部維護。
  • 當 API 中可以更改的部分已被識別並需要解耦時。

您的函數將models.User的值轉換為interface ,然后返回interface值。 這就是為什么它很貴。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM