为什么解组会在golang中更改对象的类型
我想编写一个mockData方法,它可以接受多种类型的参数,并根据其json数据返回相应的对象。代码如下:为什么解组会在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{}
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
}
摘要:
&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
?因为它是指向结构的指针,而不是指向接口的指针。