json - 将嵌套接口(interface){}解码为用户提供的结构指针

标签 json go reflection

我有一个 Set 函数,它在我自己的名为 session 的结构中包装了一个用户对象(或变量)。它将它分配给我的 session 结构的值字段。然后 Set 函数编码此结构并将字符串分配到存储中的某处。

我的问题是我不确定如何实现我的 Get 函数以仅返回存储在值字段中的未编码结构,而不是整个 session 包装器结构。

我做了一个非常简单的 example证明我在说什么。

我无法在 Get 函数的赋值中使用类型断言,因为我事先不知道用户将使用什么类型。

我怀疑可能有一种方法可以使用反射来实现这一点?

编辑: 目前提供的两个答案不是我要找的。我不知道用户将使用什么类型,它可能是任何类型,因此通过硬编码他们的类型或试图“猜测”它可能包含的内容来围绕它进行编码是行不通的。

最佳答案

好的,我想我知道你想做什么。我找到了这个答案 Converting map to struct并进行了一些调整以使其适用于您的特定用例。注意:这还没有经过彻底测试,可能有点不稳定,使用风险自负:

package main

import (
    "bytes"
    "encoding/json"
    "errors"
    "fmt"
    "log"
    "reflect"
)

type session struct {
    Value interface{}
    Flash map[string]string
}

type Person struct {
    Name string
    Age  int
}

func Get(pointer interface{}) {
    marshalledString := `{"Value":{"Name":"bob","Age":3},"Flash":null}`

    var sess session

    d := json.NewDecoder(bytes.NewBuffer([]byte(marshalledString)))
    d.UseNumber()
    if err := d.Decode(&sess); err != nil {
        panic(err)
    }

    fmt.Printf("%#v", sess)

    switch sess.Value.(type) {
    case map[string]interface{}:
        err := FillStruct(sess.Value.(map[string]interface{}), pointer)
        if err != nil {
            log.Fatal(err)
        }
    default:
        return // You may want to return an error here...
    }
}

func main() {
    var personObj Person

    Get(&personObj)

    // Wanting to see personObj here have Name "bob" and Age 3
    fmt.Printf("%#v", personObj)
}

func SetField(obj interface{}, name string, value interface{}) error {
    structValue := reflect.ValueOf(obj).Elem()
    structFieldValue := structValue.FieldByName(name)

    if !structFieldValue.IsValid() {
        return fmt.Errorf("No such field: %s in obj", name)
    }

    if !structFieldValue.CanSet() {
        return fmt.Errorf("Cannot set %s field value", name)
    }

    structFieldType := structFieldValue.Type()
    val := reflect.ValueOf(value)

    if _, ok := value.(json.Number); ok {
        if f, err := value.(json.Number).Int64(); err == nil {
            structFieldValue.SetInt(f)
            return nil
        }
        if f, err := value.(json.Number).Float64(); err == nil {
            structFieldValue.SetFloat(f)
            return nil
        }
    }

    if structFieldType != val.Type() {
        return errors.New(fmt.Sprintf("Provided value type [%s] didn't match obj field type [%s]", val.Type().String(), structFieldType.String()))
    }

    structFieldValue.Set(val)
    return nil
}

func FillStruct(m map[string]interface{}, s interface{}) error {
    for k, v := range m {
        err := SetField(s, k, v)
        if err != nil {
            return err
        }
    }
    return nil
}

关于json - 将嵌套接口(interface){}解码为用户提供的结构指针,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41157259/

相关文章:

javascript - 遍历 JSON 数组,显示两个然后计数

go - 使用缓冲 IO 与使用 Goroutine 写入文件

go - v.Type().Elem() 和 v.Elem().Type() 有什么区别?

java - RMI + java 反射

c# - 获取扩展方法的 MethodInfo

android - 如何在android中的sqlite中保存来自json的数据

c# - 从 JSON 字符串中提取值 c#

java - 已部署资源的相对路径 Tomcat 7

http - 如何在 golang 中获取 http 请求的初始时间戳?

go - 不使用 CGo 将 Go 字符串转换为 C 字符串