
JSON.Unmarshal 传指针还是传值?
传值会失败,必须传指针。Go 的 json.Unmarshal 内部靠反射修改目标变量的内存内容,如果传入的是值(比如 user 而不是 &user),它只能修改栈上的一份副本,原变量不受影响。
- 常见错误现象:
json.Unmarshal([]byte(`{"name":"a"}`), user)后user.Name仍是空字符串,无报错但无效果 - 值类型(如
struct、int、string)必须取地址传入;引用类型(如*struct、[]int、map[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 的切片;如果是null,s保持 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 } - 还要注意:如果字段有指针或嵌套自定义类型,别名方式可能丢失部分逻辑,建议只在必要时覆盖,其余字段尽量委托给默认行为










