结构体字段必须首字母大写,否则json.unmarshal静默忽略并保持零值;嵌套字段、容器层和动态key均需遵循导出规则与显式标签映射。

结构体字段必须首字母大写,否则 json.Unmarshal 什么也填不上
这是最常被忽略的硬性规则:Go 的 encoding/json 包完全无法访问小写字母开头的字段。哪怕你写了 json:"name" 标签,name string 依然会被静默跳过,值永远是零值(空字符串、0、nil 切片)。
常见错误现象:Gateways 切片为空、CreatedAt 始终为 0、嵌套对象字段全 nil —— 十有八九是字段没导出。
- ✅ 正确写法:
Name string `json:"name"`、User User `json:"user"` - ❌ 错误写法:
name string `json:"name"`、user User `json:"user"` - ⚠️ 注意:嵌套结构体里的所有字段也必须首字母大写,否则内层照样不生效
嵌套对象必须用命名 struct,别用匿名 struct 或嵌入
json.Unmarshal 不会自动“展开”或“提升”层级。它严格按结构体字段名 + json: 标签匹配当前 JSON 层级的 key。把 Nest Nest `json:"nest"` 写成 Nest struct{ID string} `json:"nest"`,Go 会当成嵌入(embedding),期望 JSON 是 {"id":"123"},而不是 {"nest":{"id":"123"}} —— 结果就是字段解不出来。
使用场景:API 返回带 "data"、"payload"、"user" 等容器层的响应。
- ✅ 正确:外层定义
User User `json:"user"`,再单独定义type User struct { Name string `json:"name"` } - ❌ 错误:直接写
User struct{ Name string } `json:"user"` - ⚠️ 混合结构中,若某层纯属容器(如
"result"),可保留该字段但不拆解,避免过度展开
动态 key(比如 ID 当键)只能用 map[string]T,不是 map[string]interface{}
看到类似 {"1001": {"name": "A"}, "2002": {"name": "B"}} 这种结构,第一反应不是切片,而是 map[string]User。用 []User 或 []struct{} 会直接报错:json: cannot unmarshal object into Go value of type []User。
性能影响:用 map[string]User 解析比 map[string]interface{} 快且安全 —— 后者下一层取值还得反复断言,极易 panic。
- ✅ 正确:
type UserMap map[string]User,然后json.Unmarshal(data, &userMap) - ❌ 错误:
map[string]map[string]interface{}或map[string]interface{} - ⚠️ JSON 标准只允许字符串 key,服务端返回数字 key 实际已转成字符串,Go 侧一律按
string处理即可
数组里混着不同结构?用 json.RawMessage 延迟解析
当 JSON 数组元素类型不一致,比如 [{"type":"user","name":"A"},{"type":"admin","level":9}],直接解到统一 struct 必然失败:json: cannot unmarshal object into Go struct field。
不能靠运行时判断 Type 字段再重解;而应让 json.RawMessage 把原始字节缓存下来,后续按需解析。
- ✅ 正确:字段声明为
Items []json.RawMessage `json:"items"`,遍历时对每个item先json.Unmarshal(item, &t)提取Type,再二次解码到对应目标 struct - ⚠️ 关键点:
json.RawMessage是[]byte别名,不触发解析,零拷贝;但它只能作为 struct 字段类型,不能用于局部变量直接json.Unmarshal - ⚠️ 容易被忽略:
json.RawMessage本身没状态,可以多次用,但别误以为是“可重读流”——每次Unmarshal都是重新解析原始字节
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











