json.marshal只序列化首字母大写的导出字段,小写字段被静默忽略;需确保字段导出、正确使用json tag(如json:"user_name")、注意类型匹配与零值行为。

json.Marshal 是最常用也最容易出错的入口,它不会报错但会静默丢字段——只要结构体字段名小写、没打 json 标签、或类型不匹配,输出里就直接消失。
struct 字段不导出导致序列化为空
Go 的 json.Marshal 只处理首字母大写的导出字段。小写字母开头的字段(如 email、createdAt)会被完全忽略,不报错也不警告。
常见错误现象:
- 结构体明明有值,
json.Marshal输出却是{}或缺失关键字段 - 对接外部 API 时字段名必须是
user_name,但 Go 字段写成UserName导致映射失败
解决办法:
- 确保字段名首字母大写(
UserName✅,username❌) - 用
json:"user_name"标签显式指定 key 名 - 敏感字段加
json:"-"彻底排除(如密码、内部 ID) - 零值字段想省略就加
json:",omitempty",但注意bool、int、string的零值含义不同
嵌套 struct 和 interface{} 的坑
嵌套结构体字段也得导出,否则内层字段全丢;而 map[string]interface{} 虽灵活,但反序列化后需大量类型断言,且 json.Unmarshal 对 nil slice/map 不自动初始化。
典型问题:
- 结构体含
Data map[string]interface{},解出来是nil,取Data["id"]panic - 嵌套 struct 字段是
Info *UserInfo,但Info为nil,json.Marshal输出"info":null,下游解析失败
建议做法:
- 避免裸用
interface{},优先定义具体 struct(如type ResponseData struct { ... }) - 需要动态键时,用
json.RawMessage延迟解析,而不是直接塞进interface{} - 初始化嵌套指针字段:在 struct 初始化时手动赋
&UserInfo{},或用构造函数封装
time.Time 和自定义类型必须显式处理
time.Time 默认只认 RFC3339 格式("2006-01-02T15:04:05Z"),遇到 "2006-01-02" 或 "2006-01-02 15:04:05" 直接静默失败,字段变零值。
错误表现:
- JSON 里时间字段是
"created_at":"2025-10-20",解出来CreatedAt却是0001-01-01 - 用
string字段暂存时间再手动 parse,后续逻辑漏判空或格式错,引发 panic
正确姿势:
- 定义新类型包装
time.Time,实现UnmarshalJSON支持多种格式 - 生成 JSON 时若需定制格式,同样重写
MarshalJSON - 数据库字段允许 NULL?用
*time.Time或sql.NullTime,别用值类型硬扛
gob 序列化只适合 Go 内部通信
gob 性能高、保真强,但它是 Go 专属协议:不能跨语言,类型信息(包路径、字段顺序、大小写)必须严格一致,否则 Decode 会 EOF 或 panic。
高频踩坑点:
- 编码端是
type User struct{ Name string },解码端改成type UserInfo struct{ Name string }—— 类型不匹配,直接失败 - 结构体加了新字段但没放末尾,或改了字段类型(
int→int64),旧数据无法 decode - 传
map[string]interface{}给gob.Encoder——gob不支持未注册的空接口,panic
适用场景:
- Redis 存储(配合
base64转字符串) - 同一服务内进程间通信(如 worker pool 传任务)
- 本地文件缓存(非人类可读,但体积小、速度快)
别用在 HTTP API、跨语言 RPC 或需要长期兼容的存储场景。
真正麻烦的不是怎么写 json.Marshal,而是字段导出规则、标签语义、零值行为和类型对齐这四点混在一起时,错误不报、结果不对、排查费劲。尤其当结构体来自 ORM 或第三方 SDK,字段命名和导出状态不受控,更要提前检查。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











