为什么解组会在golang中更改对象的类型

为什么解组会在golang中更改对象的类型,go,interface,unmarshalling,Go,Interface,Unmarshalling,我想编写一个mockData方法,它可以接受多种类型的参数,并根据其json数据返回相应的对象。代码如下: func MockData(jsonPath string,v interface{})(interface{},error){ var ret interface{} data,_ := ioutil.ReadFile(jsonPath) switch v.(type) { case Req: ret = Req{}

我想编写一个mockData方法,它可以接受多种类型的参数,并根据其json数据返回相应的对象。代码如下:

func MockData(jsonPath string,v interface{})(interface{},error){
    var ret interface{}
    data,_ := ioutil.ReadFile(jsonPath)    
    switch v.(type) {
    case Req:
        ret = Req{}
        fmt.Printf("\n===before Unmarshal==%T===\n",ret)
        err = json.Unmarshal(data,&ret) 
        if err!=nil{...}
        fmt.Printf("======after unmarshal===%T\n",ret)
    case ...
    default:
        fmt.Printf("error===not match")
   }
   return ret,err
}
func main(){
    reqJsonPath := /xx/yy/req.json
    obj,err:=test.MockData(jsonFile,Req{})
    if err!=nil{...}
    require := obj.(Req) //panic cant []interface{} to Req
}
===before Unmarshal==*Req===
======after unmarshal===*Req
type Req []*Ele
type Ele struct {
   ID   int 
   Level int   
}
然而,当我使用它时,它会惊慌失措。代码如下:

func MockData(jsonPath string,v interface{})(interface{},error){
    var ret interface{}
    data,_ := ioutil.ReadFile(jsonPath)    
    switch v.(type) {
    case Req:
        ret = Req{}
        fmt.Printf("\n===before Unmarshal==%T===\n",ret)
        err = json.Unmarshal(data,&ret) 
        if err!=nil{...}
        fmt.Printf("======after unmarshal===%T\n",ret)
    case ...
    default:
        fmt.Printf("error===not match")
   }
   return ret,err
}
func main(){
    reqJsonPath := /xx/yy/req.json
    obj,err:=test.MockData(jsonFile,Req{})
    if err!=nil{...}
    require := obj.(Req) //panic cant []interface{} to Req
}
===before Unmarshal==*Req===
======after unmarshal===*Req
type Req []*Ele
type Ele struct {
   ID   int 
   Level int   
}
MockData的输出为:

===before Unmarshal==Req===
======after unmarshal===[]interface{}
对象的类型在解组后更改。更奇怪的是,如果我替换:

ret = Req{}

输出将如下所示:

func MockData(jsonPath string,v interface{})(interface{},error){
    var ret interface{}
    data,_ := ioutil.ReadFile(jsonPath)    
    switch v.(type) {
    case Req:
        ret = Req{}
        fmt.Printf("\n===before Unmarshal==%T===\n",ret)
        err = json.Unmarshal(data,&ret) 
        if err!=nil{...}
        fmt.Printf("======after unmarshal===%T\n",ret)
    case ...
    default:
        fmt.Printf("error===not match")
   }
   return ret,err
}
func main(){
    reqJsonPath := /xx/yy/req.json
    obj,err:=test.MockData(jsonFile,Req{})
    if err!=nil{...}
    require := obj.(Req) //panic cant []interface{} to Req
}
===before Unmarshal==*Req===
======after unmarshal===*Req
type Req []*Ele
type Ele struct {
   ID   int 
   Level int   
}
为了更方便地再现问题,我给出了如下所示的Require结构:

func MockData(jsonPath string,v interface{})(interface{},error){
    var ret interface{}
    data,_ := ioutil.ReadFile(jsonPath)    
    switch v.(type) {
    case Req:
        ret = Req{}
        fmt.Printf("\n===before Unmarshal==%T===\n",ret)
        err = json.Unmarshal(data,&ret) 
        if err!=nil{...}
        fmt.Printf("======after unmarshal===%T\n",ret)
    case ...
    default:
        fmt.Printf("error===not match")
   }
   return ret,err
}
func main(){
    reqJsonPath := /xx/yy/req.json
    obj,err:=test.MockData(jsonFile,Req{})
    if err!=nil{...}
    require := obj.(Req) //panic cant []interface{} to Req
}
===before Unmarshal==*Req===
======after unmarshal===*Req
type Req []*Ele
type Ele struct {
   ID   int 
   Level int   
}
摘要

  • 我是否可以实现预期的功能,根据json和类型生成不同类型的对象
  • 为什么对象的类型在解组后会更改,为什么在添加&后不会更改
  • 我是否可以实现预期的功能,根据json和类型生成不同类型的对象

    我真的不知道你为什么需要传递一个实际的结构而不是指针

    为什么对象的类型在解组后会更改,为什么在添加&后不会更改

    当您使用
    &ret
    解组时,
    ret
    是一个接口,您将获得接口的地址。因此,
    json.Unmarshal()。
    json.Unmarshal()
    将使用的默认数据类型是对象的
    map[string]interface{}
    ,数组的
    []interface{}

    现在,如果您使用
    ret
    进行解组,其中
    ret
    &Req{}
    json.unmarshal()
    将检查备份数据是否是
    结构,因此它可以使用结构的字段进行解组

    编辑

    您似乎被指向接口的指针弄糊涂了,该接口不同于具有指针的接口。试试这段代码,你会看到不同之处

    var x interface{} = Req{}
    var y interface{} = &x
    var z interface{} = &Req{}
    
    fmt.Printf("%T\n", y)
    fmt.Printf("%T\n", z)
    
    记住,接口只是普通值,它们也占用内存。现在,如果您获取该内存的地址,您将获得指向接口的指针,而不是指向接口所引用的数据的指针

  • 我是否可以实现预期的功能,根据json和类型生成不同类型的对象
  • 是的,但是您必须在调用端使用类型断言将其转换回来

      MyFoo:=MockData("foo.json", Foo{}).(Foo)
    
    (或在func中有多个返回返回(Foo)
    返回返回(Bar)

  • 为什么对象的类型在解组后会更改,为什么在添加&后不会更改
  • 在解组源代码的顶部有一些有用的注释 即

    因此,在第一种情况下,您将解组成一个接口值(
    ret
    被声明为接口{})
    在第二种情况下,有一个指向结构的指针,这就是您得到的

    变量
    ret
    的类型是
    interface{}
    ,该类型用于解组到。反射是复杂的,您的printf不能像您期望的那样工作。阅读博客文章的反思法则。您必须实际解组到一个common.Require(而不是一个空接口)中,因此将变量decl移动到类型开关中。哪里是
    common.Require
    defined?@Flimzy oh,为了简化问题,我将common.Require替换为Req,我将更新它。如果我使用
    ret
    解组,
    &ret
    也是接口的地址。为什么
    json.Unmarshal()
    会检查备份数据是否为
    struct
    ?因为它是指向结构的指针,而不是指向接口的指针。