Go语言中json.Unmarshal解析JSON数组必须严格匹配结构:数组对应[]T切片,对象对应struct;用结构体接收纯数组会失败,需确保JSON根类型与Go目标类型层级一致。

直接用 json.Unmarshal 解析到结构体,别先转 map[string]interface{}——这是最稳、最类型安全、IDE 支持最好的路。硬解成 map 看似灵活,实则每层取值都要断言,一漏就 panic,后期改字段名或加校验全得重写。
结构体必须严格镜像 JSON 层级
Go 的 json.Unmarshal 不会自动“穿透”嵌套对象。它只认你写的字段名(或 json:"key" 标签)是否与当前层级的 JSON key 一致。
- 错误写法:
Nest struct { ID string }—— 缺少字段名,Go 当成嵌入,期望 JSON 是{"ID":"123"},但实际是{"Nest":{"ID":"123"}},结果字段为空 - 正确写法:
Nest Nest `json:"Nest"`,再配独立的type Nest struct { ID string `json:"ID"` } - 所有字段首字母必须大写,否则
encoding/json完全不可见,值永远是零值 - 嵌套数组(如
"items": [{"id":1}, {"id":2}])对应Items []Item `json:"items"`,不是[]map[string]interface{}
字段名不匹配?必须用 json tag 显式映射
API 返回的 key 多为小写下划线(created_at、user_id),而 Go 字段名按惯例是 CreatedAt、UserID。不加标签,Unmarshal 就不会填充。
CreatedAt int64 `json:"created_at"`UserID string `json:"user_id"`- 空标签
json:""或省略标签 = 按字段名匹配(大小写敏感),基本不用 - 标签里加
,omitempty只影响序列化(Marshal),对反序列化(Unmarshal)无作用
遇到动态 key 或多态字段怎么办
JSON 里出现 {"1001": {...}, "1002": {...}} 或 "status": null / "status": "ok" / "status": 404 这类场景,不能硬塞一个固定类型。
- 动态 key(ID 作键)→ 用
map[string]T,其中T是具体结构体,比如type UserMap map[string]User;别用map[string]map[string]interface{},否则下一层又得断言 - 可能为
null、缺失、或多种类型 → 字段声明为*json.RawMessage,后续按需解析;例如RT *json.RawMessage `json:"RT"`可安全接收null、"abc"、{}任意值 - 数组元素结构不一致(如
[{"type":"user"}, {"type":"admin","privs":[]}])→ 先用[]json.RawMessage接住,再遍历每个元素,根据type字段分支解析到不同 struct
深层嵌套时别堆匿名 struct,拆成具名类型
写 Results struct { Quotes []struct{ LastTradePriceOnly string } `json:"quote"` } 看似省事,但没法加方法、不能复用、单元测试难写、IDE 补全失效。
- 把每一层有意义的嵌套都提成
type Quote struct { LastTradePriceOnly string `json:"LastTradePriceOnly"` } - 外层用
Quotes []Quote `json:"quote"`,清晰且可维护 - 纯容器层(如
"data"、"payload")若无业务含义,可用json:"data"标签跳过命名,但不要删掉字段 -
json.RawMessage字段必须定义在 struct 里,不能用于局部变量直接Unmarshal,它是[]byte别名,靠零拷贝保留原始字节
最容易被忽略的是:空嵌套对象 {} 解到 struct 得到全零值,解到 map[string]interface{} 得到空 map,行为不同;还有 nil slice 和 []T{} 在 JSON 中都表现为 [],但 Go 里语义完全不同——前者是未初始化,后者是已初始化空切片,判空逻辑要区分清楚。


















