当前位置: 首页 > 知识库问答 >
问题:

当字段实现UnmarshalJSON时,解封嵌入式字段指针panics

郗欣嘉
2023-03-14

我有一个struct,它嵌入一个指向另一个struct的嵌入指针。当我使用默认的json.unmarshal行为时,它可以完美地工作。但是,当我为嵌入式struct的类型而不是为外部struct实现unmarshaljson时,就会出现空指针取消引用的恐慌。

如果我也为外部struct类型实现unmarshaljson,那么它就可以工作了。但是,外部结构有许多字段,我不想手动解封。

  1. 为什么在其中一个而不是另一个上实现unmarshaljson会导致恐慌?
  2. 有没有一种方法可以让它工作而不为外部类型实现unmarshaljson
  3. 如果没有,是否有更简单/更少手动的方法来为外部类型实现unmarshaljson

注意:这里有一个标题类似的问题,“当嵌入式类型具有UnmarshalJSON时,JSON.Unmarshal失败”,但这里的问题与我的问题不同。

TL;DR:这个问题的其余部分只是上面的一个冗长的例子。

示例的play.golang.org版本)

(例如,简化了--它并不真正需要自己的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}
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
[...]
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
}

但是,如果真正的容器和真正的obj都有比这更多的字段,每个字段都有不同的类型,那么以这种方式手动解封容器就会变得乏味。

问题:有没有更简单的方法来防止这种恐慌?

共有1个答案

长孙玉泽
2023-03-14

因为默认行为检查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字段不是“匿名字段”,而是嵌入式字段:https://golang.org/ref/spec#struct_types

 类似资料:
  • 问题内容: JPA实体类是否可以包含两个嵌入式()字段?一个例子是: 在这种情况下,a 可以包含两个实例-家庭和工作。我在Hibernate的实现中使用JPA。当我使用Hibernate Tools生成架构时,它仅嵌入一个。我想要的是两个嵌入式实例,每个实例的列名都经过区分或在前面加上一些前缀(例如home和work)。我知道,但是这要求每个属性都应单独覆盖。如果嵌入的对象()变大,那么这会变得很

  • 操作步骤: 菜单栏: Refactor —> Encapsulate Field ...

  • 问题内容: 我有一个结构: 我在全球范围内存储一部分用户: 最后,http处理程序: 所以,为什么会这样,我的意思是我发送“加载”请求,它加载数据,设置为。但是在下一次调用“ newData”请求时,是? 以防万一,这里是: 编辑: 哪里来的: 问题答案: 这是因为您对 副本 进行操作,而不是对slice元素本身进行操作。 在函数中,make循环遍历的元素的副本,然后返回此副本的地址。因此,稍后您

  • 问题内容: 我正在研究一个示例程序来回答关于SO的另一个问题,但发现自己对以下代码无法编译的事实感到困惑。 https://play.golang.org/p/wxBGcgfs1o 错误是; 是否可以在静态初始化程序中从嵌入式类型设置字段的值?怎么样?在我看来,这似乎是一个编译器错误;如果我前面没有源代码并且熟悉类型,那么我会在墙上碰头说“显然FName存在于B上,编译器在说什么!!!!!”。 很

  • 问题内容: 我想在结构上定义一个方法来验证http请求。但是我在访问结构域时遇到一些问题。 有我的代码。 运行此代码时,得到以下结果 有什么方法可以访问Validate2()方法上的Validate()方法上的结构字段? 问题答案: 您不能从内部结构访问外部结构字段。仅内部字段来自外部。您可以做的是:

  • 问题内容: 背景 我已经做了大量的规范阅读和代码测试,我认为答案是否定的,但是我想确保自己没有遗漏任何东西。 目标 基本上,我正在尝试为Go创建Active Record样式的ORM,因为我喜欢它的可读性以及从后端数据存储中抽象出来的方式。我宁愿写,也不愿在用户结构上嵌入常见的CRUD方法。 例 http://play.golang.org/p/cWyqqVSKGH 题 有没有一种方法可以使嵌入式