我有一个struct
,它嵌入了指向另一个struct
的嵌入式指针。当我使用默认的json.Unmarshal
行为时,它可以完美运行。但是,当我为 embedded 的类型实现UnmarshalJSON
而不是外部struct
的类型struct
时, ,然后使用空指针取消引用进行恐慌。
如果我也为外部UnmarshalJSON
类型实现了struct
,那么它将起作用。但是,外部结构有很多字段,我不希望手动解组。
UnmarshalJSON
会引起恐慌?UnmarshalJSON
的情况下,有没有办法使它工作?UnmarshalJSON
?注意: 有一个标题相似的问题“ json.Unmarshal fails when embedded type has UnmarshalJSON ”,但该问题不同于我的问题。
tl; dr:这个问题的其余部分只是上面的冗长示例。
(例如play.golang.org version)
两个结构,一个结构具有指向另一个的嵌入式字段指针:
(例如,经过简化-并不需要它自己的UnmarshalJSON
,但它可以证明问题所在。)
type Obj struct {
X int `json:"x"`
}
type Container struct {
*Obj
Y int `json:"y"`
}
调用元帅:
func main() {
b := []byte(`{"x": 5, "y": 3}`)
c := &Container{}
err := json.Unmarshal(b, c)
if err != nil {
fmt.Printf("error ummarshalling json: %+v\n", err)
return
}
fmt.Printf("unmarshalled: %+v --> %+v\n", c, c.Obj)
}
无需实现任何UnmarshalJSON
函数,就可以正常工作:
unmarshalled: &{Obj:0x416080 Y:3} --> &{X:5}
但是,如果仅将UnmarshalJSON
添加到嵌入式Obj
类型中,那么程序将陷入混乱,因为json.Unmarshal
调用试图解组时传递了一个nil
指针*Obj
。
func (o *Obj) UnmarshalJSON(b []byte) (err error) {
m := make(map[string]int)
err = json.Unmarshal(b, &m)
if err != nil {
return nil
}
o.X = m["x"] // the line indicated by panic
return nil
}
输出:
panic: runtime error: invalid memory address or nil pointer dereference
[...]
main.(*Obj).UnmarshalJSON(0x0, 0x416030, 0x10, 0x10, 0x0, 0x0)
/tmp/sandbox185809294/main.go:18 +0x130
[...]
问题:为什么在这里会出现恐慌,但默认的解组行为却不会呢?我认为如果在这里传递nil
*Obj
,则默认行为也会绕过nil
指针...
当我为外部UnmarshalJSON
类型实现Container
时,它不再惊慌:
func (c *Container) UnmarshalJSON(b []byte) (err error) {
m := make(map[string]int)
err = json.Unmarshal(b, &m)
if err != nil {
return err
}
c.Obj = &Obj{X: m["x"]}
c.Y = m["y"]
return nil
}
但是,如果实数Container
和实数Container
都具有比这更多的字段,并且每个字段都具有不同的类型,则以这种方式手动解组Obj
变得很乏味。
问题:有没有更简单的方法来防止这种恐慌?
答案 0 :(得分:1)
因为默认行为会检查nil
,而您的自定义解组器则不会。您需要在UnmarshalJSON
中添加一些逻辑,以检查o
是否为nil
并正常运行,而不是假设o
不是nil
(尝试访问一个{的字段),从而引发恐慌。
func (o *Obj) UnmarshalJSON(b []byte) (err error) {
if o == nil {
return nil // maybe? What do you want to happen in this case?
}
m := make(map[string]int)
err = json.Unmarshal(b, &m)
if err != nil {
return nil
}
o.X = m["x"] // the line indicated by panic
return nil
}
也仅供参考,您的*Obj
字段不是“匿名字段”,而是 embedded 字段:https://golang.org/ref/spec#Struct_types