json.Marshal返回空对象{}是因为结构体字段未导出(首字母小写),encoding/json包无法通过反射访问私有字段,必须将字段名首字母大写并配合json标签(如Name string json:"name")才能正确序列化。

json.Marshal 为什么返回空对象 {}
结构体字段没导出,json.Marshal 就会静默忽略全部字段,输出 {}。这不是 bug,是 Go 反射机制的硬性限制:小写字母开头的字段(如 name string)无法被 encoding/json 包访问。
必须把字段名首字母大写,再用 json 标签控制键名:
-
Name string `json:"name"`—— 字段导出 + 键名保持小写,符合 API 规范 - 第三方结构体改不了字段?只能中转走
map[string]interface{} - 嵌套结构里某一层字段小写?同样失效,得逐层检查
json.Unmarshal 传值还是传指针
json.Unmarshal 必须传指针,否则解析成功但目标变量仍是零值。常见错误是写成 json.Unmarshal(data, p)(p 是结构体变量),编译不报错,运行后 p.Name 还是空字符串、p.Age 还是 0。
正确写法只有一种:
立即学习“go语言免费学习笔记(深入)”;
-
err := json.Unmarshal(data, &p)——&p是指向结构体的指针 - 切片同理:
var users []User; json.Unmarshal(data, &users) - map 也一样:
var m map[string]string; json.Unmarshal(data, &m)
omitempty 在数值字段上容易误用
omitempty 会让零值字段不出现在序列化结果里,但 Age int `json:"age,omitempty"` 遇到婴儿年龄为 0 时,字段直接消失,API 接收方根本收不到这个合法值。
慎用场景:
- 所有数值类型(
int、float64、bool)——除非业务上明确“0 = 未提供” -
time.Time字段加omitempty会丢掉默认零值时间,建议用自定义MarshalJSON - 想省略空字符串但保留 0?得用
*string或自定义方法,别硬套omitempty
遇到动态或不确定结构怎么办
当 JSON 的某个字段可能是对象、数组、字符串甚至 null(比如通用 API 的 "data" 字段),直接反序列化到固定结构体会 panic 或静默失败。
推荐两种务实解法:
- 用
json.RawMessage延迟解析:Data json.RawMessage `json:"data"`,之后根据len(Data)和首字符({、[、")再决定怎么json.Unmarshal - 顶层分发用
map[string]interface{},但嵌套深了要反复类型断言,容易写崩;别在核心逻辑里多层嵌套用它
真正卡住你的往往不是语法,而是字段到底有没有进 Marshal 结果——最简单的验证方式:打印 len(b),比翻文档快得多;反序列化后立刻检查关键字段是否仍为零值,比依赖测试用例更直接。


















