Go中处理JSON应优先使用json.RawMessage延迟解析,避免硬套结构体;需检查len(raw)>0防null导致静默失败,字段必须首字母大写且正确打json标签,r.Body为单次读取流不可重复消费。

Go 里处理 JSON 不靠“优雅”这个词撑场面,靠的是选对方式、避开坑、不硬刚结构不确定性。
用 json.RawMessage 延迟解析嵌套字段
当你面对的 JSON 中某些字段结构多变(比如 "data" 有时是对象、有时是数组、甚至可能是 null),别急着塞进 struct。直接用 json.RawMessage 把原始字节扣下来,等真正需要时再解。
- 必须检查
len(raw) > 0:JSON 中的null会被解成空[]byte,直接json.Unmarshal会静默失败 - 别对
json.RawMessage做string()打印或正则匹配——它不保证格式化,可能含换行/空格,也不代表合法 JSON - 如果 key 集合有限(如只可能是
"user"/"config"/"device"),先用map[string]json.RawMessage提取,再分发给对应解析函数,比全用interface{}更易维护
结构体字段必须可导出 + 正确打标签
字段首字母小写?json.Unmarshal 会完全忽略它,解完还是零值。没加 json:"field_name" 标签?默认用 Go 字段名当 JSON key,大小写、下划线都对不上。
- 字段必须首字母大写(可导出),否则反序列化永远为空
-
omitempty对指针类型也生效,比如*string为nil时自动跳过,适合真正可选字段 - 数值字段想编码成 JSON 字符串(如
"discount": "9.99"),加json:",string"标签即可 -
json:"-"表示彻底忽略该字段,哪怕它有值
HTTP handler 里别碰 req.ParseForm()
它专为 application/x-www-form-urlencoded 和 multipart/form-data 设计。对 JSON 请求调用它,整个 {"name":"alice"} 会被当成一个表单键塞进 req.Form,值却是空切片——你得手动遍历键、再反序列化,低效且易错。
- 永远先检查
r.Header.Get("Content-Type")是否包含"application/json",否则直接返回http.StatusUnsupportedMediaType - 用
json.NewDecoder(r.Body).Decode(&v)流式解码,不缓存全部 body 到内存 -
r.Body是单次读取流:一旦被Decode消费完,再次读就是io.EOF;别在中间插io.ReadAll或req.ParseForm() - 若需复用 body(比如记录日志 + 解码),必须用
io.TeeReader或提前bytes.Buffer缓存,但会牺牲流式优势
同一字段类型不固定时,用两阶段解析
第三方 API 返回的 "items" 字段,有时是对象,有时是数组?别试图定义一个字段兼容两种类型。Go 的 Unmarshal 不支持这种歧义。
- 第一阶段:用
interface{}接收,再用类型断言判断是map[string]interface{}还是[]interface{} - 第二阶段:分别用
json.Unmarshal解到对应的目标 struct 或 slice 类型 - 避免用反射做通用转换——它掩盖错误、难调试、性能差,且无法静态检查字段是否存在
- 如果分支逻辑稳定(比如只有两种形态),可封装成带 error 返回的 helper 函数,比散落各处的类型断言更可控
最常被忽略的不是语法,而是 r.Body 的一次性本质和 json.RawMessage 的空字节边界。这两点踩中任何一个,问题就从“解析失败”变成“无声静默”。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











