【问题标题】:Unmarshal json to reflected struct将 json 解组为反射结构
【发布时间】:2022-01-08 12:33:44
【问题描述】:

是否可以在不硬编码原始类型的情况下将 JSON 解组为由反射制成的结构?

package main

import (
  "fmt"
  "encoding/json"
  "reflect"
)

type Employee struct {
  Firstname string     `json:"firstname"`
}

func main() {
  //Original struct
  orig := new(Employee)

  t := reflect.TypeOf(orig)
  v := reflect.New(t.Elem())

  //Reflected struct
  new := v.Elem().Interface().(Employee)

  // Unmarshal to reflected struct
  json.Unmarshal([]byte("{\"firstname\": \"bender\"}"), &new)

  fmt.Printf("%+v\n", new)
}

在此示例中,我使用了对 Employee 的强制转换。但是如果我不知道类型怎么办?

当我只使用v 进行解组时,结构将被清零。

json.Unmarshal([]byte("{\"firstname\": \"bender\"}"), v)

当我省略演员表时,我会得到一张地图。这是可以理解的

json.Unmarshal([]byte("{\"firstname\": \"bender\"}"), v.Elem().Interface())

【问题讨论】:

  • 仅仅因为它真的伤害了我的眼睛:为什么你有一个名为new的变量?与更灵活的&Employee{} 相比,您使用new(Employee) 有什么意义?
  • new(Something) 优于 &Something{}。它与非结构类型一致 + 它适用于该工作。

标签: json go reflection unmarshalling


【解决方案1】:

这里的问题是,如果在这里省略类型断言:

new := v.Elem().Interface()

new 被推断为具有interface{} 类型。

那么当你把地址解组时,&new 的类型是*interface{}(指向接口{}的指针),解组就不会像你期望的那样工作。

如果您直接使用指针引用而不是获取Elem(),则可以避免类型断言。

func main() {
  //Original struct
  orig := new(Employee)

  t := reflect.TypeOf(orig)
  v := reflect.New(t.Elem())

  // reflected pointer
  newP := v.Interface()

  // Unmarshal to reflected struct pointer
  json.Unmarshal([]byte("{\"firstname\": \"bender\"}"), newP)

  fmt.Printf("%+v\n", newP)
}

游乐场:https://play.golang.org/p/lTBU-1PqM4

【讨论】:

    【解决方案2】:
    
    // https://github.com/xiaojun207/go-base-utils/blob/master/utils/Clone.go
    func NewInterface(typ reflect.Type, data []byte) interface{} {
        if typ.Kind() == reflect.Ptr {
            typ = typ.Elem()
            dst := reflect.New(typ).Elem()
            json.Unmarshal(data, dst.Addr().Interface())
            return dst.Addr().Interface()
        }else {
            dst := reflect.New(typ).Elem()
            json.Unmarshal(data, dst.Addr().Interface())
            return dst.Interface()
        }
    }
    
    type Employee struct {
        Firstname string     `json:"firstname"`
    }
    
    func main() {
        data := "{\"firstName\": \"John\"}"
        obj := NewInterface(reflect.TypeOf(Employee{}), []byte(data))
        fmt.Println("Employee:", obj)
    }
    

    【讨论】:

    • 虽然此代码可能会回答问题,但提供有关它如何和/或为什么解决问题的额外上下文将提高​​答案的长期价值。
    • 您的答案可以通过额外的支持信息得到改进。请edit 添加更多详细信息,例如引用或文档,以便其他人可以确认您的答案是正确的。你可以找到更多关于如何写好答案的信息in the help center
    【解决方案3】:

    如果您根本不知道类型,您可以将 JSON 字符串解组到一个接口{}。如果您随后需要处理 Unmarshaled 数据,可以将其转换为所需的类型。

    这是一个例子:

    package main
    
    import (
        "encoding/json"
        "fmt"
        "reflect"
        "unsafe"
    )
    
    type Employee struct {
        Firstname string `json:"firstName"`
    }
    
    func deserialize(jsonData string) interface{} {
        var obj interface{}
    
        if err := json.Unmarshal([]byte(jsonData), &obj); err != nil {
            panic(err)
        }
    
        return obj
    }
    
    func NewEmployee(objData map[string]interface{}) *Employee {
        s := (*Employee)(nil)
        t := reflect.TypeOf(s).Elem()
        employeePtr := reflect.New(t)
        employee := (*Employee)(unsafe.Pointer(employeePtr.Pointer()))
        employee.Firstname = objData["firstName"].(string)
    
        return employee
    }
    
    func main() {
        jsonData := "{\"firstName\": \"John\"}"
    
        obj := deserialize(jsonData)
    
        objData := obj.(map[string]interface{})
        employee := NewEmployee(objData)
    
        fmt.Printf("%s\n", employee.Firstname)
    }
    

    您可以在Go Playground上查看。

    【讨论】:

      猜你喜欢
      • 2018-07-16
      • 1970-01-01
      • 2014-04-27
      • 1970-01-01
      • 2018-03-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-12-27
      相关资源
      最近更新 更多