【问题标题】:Unmarshal json to reflected struct (continued)将 json 解组为反射结构(续)
【发布时间】:2018-07-16 02:33:21
【问题描述】:

我想编写一个 gin 中间件处理程序,它从c.Request.FormValue("data") 获取数据,将其解组为一个结构(结构相当不同)并在上下文中设置一个变量(c.Set("Data",newP))。所以我搜索并写了这个:

package middleware

import (
    "reflect"
    "fmt"
    "github.com/gin-gonic/gin"
    "encoding/json"
)

//https://semaphoreci.com/community/tutorials/test-driven-development-of-go-web-applications-with-gin
//https://github.com/gin-gonic/gin/issues/420
func Data(t reflect.Type) gin.HandlerFunc {
    return func(c *gin.Context) {
        //https://stackoverflow.com/a/7855298/5257901
        //https://stackoverflow.com/a/45680060/5257901
        //t := reflect.TypeOf(orig)
        v := reflect.New(t.Elem())
        // reflected pointer
        newP := v.Interface()

        data:=c.Request.FormValue("data")
        fmt.Printf("%s data:%s\n",c.Request.URL.Path,data)
        if err:=json.Unmarshal([]byte(data),newP); err!=nil{
            fmt.Printf("%s data unmarshall %s, data(in quotes):\"%s\"",c.Request.URL.Path,err,data)
            c.Abort()
            return
        }
        ustr, _:=json.Marshal(newP)
        fmt.Printf("%s unmarshalled:%s\n",c.Request.URL.Path,ustr)
        c.Set("Data",newP)
        c.Next()
    }
}

我是这样使用它的:

func InitHandle(R *gin.Engine) {
    Plan := R.Group("/Plan")
    Plan.POST("/clickCreate",middleware.Data(reflect.TypeOf(new(tls.PlanTabel))), clickCreatePlanHandle)
}

var data = *(c.MustGet("Data").(*tls.PlanTabel))

这是相当沉重和丑陋的。我想要

middleware.Data(tls.PlanTabel{})

var data = c.MustGet("Data").(tls.PlanTabel)

换句话说,省略杜松子酒,我想要一个吃i interface{}并返回一个函数(data string) (o interface{})的闭包

func Data(i interface{}) (func (string) (interface{})) {
    //some reflect magic goes here
    //extract the structure type from interface{} :

    //gets a reflect type pointer to it, like
    //t := reflect.TypeOf(orig)
    return func(data string) (o interface{}) {
        //new reflected structure (pointer?)
        v := reflect.New(t.Elem())
        //interface to it
        newP := v.Interface()
        //unmarshal
        json.Unmarshal([]byte(data),newP);
        //get the structure from the pointer back

        //returns interface to the structure

        //reflect magic ends
    }
}

【问题讨论】:

    标签: json go reflection unmarshalling go-gin


    【解决方案1】:

    问题中的代码很接近。

    试试下面的功能。结果函数返回的类型与i的类型相同:

    func Data(i interface{}) func(string) (interface{}, error) {
        return func(data string) (interface{}, error) {
            v := reflect.New(reflect.TypeOf(i))
            err := json.Unmarshal([]byte(data), v.Interface())
            return v.Elem().Interface(), err
        }
    }
    

    使用示例:

    type Test struct {
        A string
        B string
    }
    
    f := Data((*Test)(nil))
    v, err := f(`{"A": "hello", "B": "world"}`)
    if err != nil {
        log.Fatal(err)
    }
    fmt.Printf("%#v\n", v) // v is a *Test
    
    f = Data("")
    v, err = f(`"Hello"`)
    if err != nil {
        log.Fatal(err)
    }
    fmt.Printf("%#v\n", v)  // v is a string
    

    如果要返回结构体值,则将结构体值作为参数传递给 Data:

    f = Data(Test{})
    v, err = f(`{"A": "hello", "B": "world"}`)
    if err != nil {
        log.Fatal(err)
    }
    fmt.Printf("%#v\n", v) // v is a Test
    

    Playground example

    【讨论】:

    • 谢谢。 return v.Elem().Interface() 有什么问题?我更喜欢它。
    • 如果应用程序正在解组大的struct 值,那么返回一个指向结构的指针通常会更好。我更新了问题以显示如何返回值。
    • @AlexeyBurdin 查看新的和改进的答案,根据数据的参数按值或指针返回结构。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-04-27
    • 1970-01-01
    • 2018-03-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-12-27
    相关资源
    最近更新 更多