为什么取消编组使golang中的对象类型更改

时间:2018-11-23 03:19:16

标签: go interface unmarshalling

我想编写一个模拟数据方法,该方法可以接受几种类型的参数并根据其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
}

并且MockData的输出是:

===before Unmarshal==Req===
======after unmarshal===[]interface{}

解组后更改的对象类型 。更奇怪的是,如果我替换:

ret = Req{}

ret = &Req{}

输出将与以下相同:

===before Unmarshal==*Req===
======after unmarshal===*Req

为了更方便地重现该问题,我给出了Require结构,如下所示:

type Req []*Ele
type Ele struct {
   ID   int 
   Level int   
}

摘要

  1. 我可以实现根据其json和类型生成不同类型对象的预期功能吗?
  2. 为什么解组后对象的类型会发生变化,为什么在添加&后它不会发生变化?

2 个答案:

答案 0 :(得分:1)

  

我能否实现根据其json和类型生成不同类型对象的预期功能?

func MockData(filename string, v interface{}) (interface{}, error) {

    data, _ := ioutil.ReadFile(filename)
    switch t := v.(type) {
    case Req:
        // t at this point is a Req{}
        err := json.Unmarshal(data, &t)
        return t, err
    }
    return nil, errors.New("unknown type")
}

我真的不了解您的动机,为什么您需要传递实际的结构而不是指针。 Check this demonstration

  

为什么解组后对象的类型会发生变化,为什么在添加&后它不会发生变化?

使用&ret(其中ret是接口)进行解组时,您将获取该接口的地址。因此,json.Unmarshal()将看到支持数据是接口而不是结构的指针。 json.Unmarshal()将使用的默认数据类型是map[string]interface{}用于对象,[]interface{}用于数组。

现在,如果您使用retret的{​​{1}}解组,&Req{}将检查后备数据是否为json.Unmarshal(),因此可以做到这一点使用该结构的字段取消编组。

修改

您似乎被指向接口的指针所迷惑,与具有指针的接口不同。尝试使用此代码,您将看到区别。

struct

请记住,接口只是普通值,它们也占用内存。现在,如果您获取该内存的地址,则将获得指向接口的指针,而不是指向该接口所引用的数据的指针。

答案 1 :(得分:0)

  1. 我可以实现根据其json和类型生成不同类型对象的预期功能吗?

是的,但是您必须在调用端使用类型声明将其转换回去,即

  MyFoo:=MockData("foo.json", Foo{}).(Foo)

(或功能中有多个return ret.(Foo) return ret.(Bar)

  1. 为什么解组后对象的类型会发生变化,为什么在添加&后它不会发生变化?

Unmarshal源代码的顶部有一些有用的评论 即

// To unmarshal JSON into a pointer, Unmarshal first handles the case of
// the JSON being the JSON literal null. In that case, Unmarshal sets
// the pointer to nil. Otherwise, Unmarshal unmarshals the JSON into
// the value pointed at by the pointer. If the pointer is nil, Unmarshal
// allocates a new value for it to point to.

// To unmarshal JSON into an interface value,
// Unmarshal stores one of these in the interface value:
//
//  bool, for JSON booleans
//  float64, for JSON numbers
//  string, for JSON strings
//  []interface{}, for JSON arrays
//  map[string]interface{}, for JSON objects
//  nil for JSON null

因此,在第一种情况下,您需要解组为接口值(ret被声明为接口{}) 在第二种情况下,有一个指向结构的指针,所以您可以得到