Go语言中的值类型与引用类型的Unmarshal区别 Golang JSON解析

6次阅读

Go语言中的值类型与引用类型的Unmarshal区别 Golang JSON解析

jsON.Unmarshal 传指针还是传值?

传值会失败,必须传指针。gojson.Unmarshal 内部靠反射修改目标变量的内存内容,如果传入的是值(比如 user 而不是 &user),它只能修改上的一份副本,原变量不受影响。

  • 常见错误现象:json.Unmarshal([]byte(`{"name":"a"}`), user)user.Name 仍是空字符串,无报错但无效果
  • 值类型(如 StructintString)必须取地址传入;引用类型(如 *struct[]intmap[string]string)本身已含指针语义,但仍需确保非 nil
  • 例如 var m map[string]string; json.Unmarshal(b, m) 会 panic:「panic: reflect: call of reflect.Value.SetMap on zero Value」——因为 m 是 nil,得先 m = make(map[string]string) 或直接传 &m

struct 字段为什么没被赋值?

字段必须是导出的(首字母大写),且 JSON key 要能匹配到。Go 的 json 包只处理导出字段,小写字段永远被忽略,无论 tag 写得多全。

  • 常见错误现象:定义 type User struct { name string `json:"name"` },反序列化后 name 始终为空
  • 必须写成 Name string `json:"name"`,大小写和导出性缺一不可
  • tag 中的 json:"name" 控制键名映射;json:"name,omitempty" 在值为零值时跳过该字段;json:"-" 彻底忽略该字段
  • 注意:嵌套 struct 的字段也要逐层导出,否则中间某一级小写会导致后续全部失效

切片和 map 解析时 nil 和空的区别

nil 切片或 map 在 Unmarshal 时会被自动分配,但前提是传入的是它们的地址;如果传的是值(比如 var s []int; json.Unmarshal(b, s)),就什么都不会发生。

  • 正确做法:var s []int; json.Unmarshal(b, &s) —— s 会变成非 nil 切片
  • 如果 JSON 是 [](空数组),s 变成长度为 0 的切片;如果是 NULLs 保持 nil(除非加 json:",string" 等特殊 tag)
  • map 同理:var m map[string]int; json.Unmarshal(b, &m) 才安全;直接传 m 不会分配,也不报错
  • 性能影响:反复 Unmarshal 到同一个变量,对 slice 会复用底层数组;但 map 每次都会新建,旧数据完全丢弃

自定义 UnmarshalJSON 方法容易漏掉什么?

实现 UnmarshalJSON 时,若想复用默认逻辑,必须手动调用 json.Unmarshal 到临时 struct,而不是直接解到 *t —— 否则会无限递归

立即学习go语言免费学习笔记(深入)”;

  • 典型错误:func (t *MyType) UnmarshalJSON(data []byte) Error { return json.Unmarshal(data, t) } → stack overflow
  • 正确写法:定义一个匿名 struct 或内部别名类型,绕过方法查找,例如:
    type myTypeAlias MyType func (t *MyType) UnmarshalJSON(data []byte) error {     var alias myTypeAlias     if err := json.Unmarshal(data, &alias); err != nil {         return err     }     *t = MyType(alias)     return nil }
  • 还要注意:如果字段有指针或嵌套自定义类型,别名方式可能丢失部分逻辑,建议只在必要时覆盖,其余字段尽量委托给默认行为

实际用的时候,最常踩的坑不是语法写错,而是忘了传指针、字段没导出、或者自定义方法里没做别名跳转——这三个地方一错,程序不报错但数据就是空,查起来特别花时间。

text=ZqhQzanResources