Go语言JSON序列化要求结构体字段必须导出(首字母大写),否则json tag无效;小写字段被忽略导致空对象或零值,Gin的c.JSON和c.ShouldBindJSON同样遵循该规则。

结构体字段加 json Tag 后仍不生效?检查导出规则
Go 语言要求 JSON 序列化时,字段必须是**导出(首字母大写)**的,否则 json Tag 完全被忽略。这是最常见却最容易被忽略的坑。
- 错误写法:
type User struct { name string `json:"name"` }→name小写,序列化后为空对象{} - 正确写法:
type User struct { Name string `json:"name"` }→ 首字母大写,Tag 才起作用 - Gin 的
c.JSON()底层用的是encoding/json,行为完全一致,不额外做反射修正
空值字段默认被忽略?用 omitempty 要谨慎
omitempty 是常用但容易误用的修饰符:它不仅跳过零值(""、0、false、nil),还会跳过指针/切片/映射的 nil 值,但不会跳过已初始化的零值。
- 比如
Age *int `json:"age,omitempty"`,如果Age == nil,字段不出现在 JSON 中;但如果Age = &zero(zero=0),字段会输出"age": 0 - 字符串字段
Name string `json:"name,omitempty"`,当Name == ""时字段直接消失,前端可能因字段缺失报错 - 若需保留空字段,去掉
omitempty,或改用指针类型 + 显式赋值控制
Gin 绑定请求体时,json Tag 控制反序列化字段名
不只是输出,json Tag 同样影响 c.ShouldBindJSON() 和 c.BindJSON() 的输入解析 —— 它按 Tag 名匹配 JSON 键,不是结构体字段名。
- 前端传
{"user_name": "alice"},结构体得写UserName string `json:"user_name"`,不能只靠字段名UserName自动映射 - Tag 中可叠加多个选项,如
`json:"user_name,omitempty" binding:"required"`,json和binding互不影响 - 注意大小写敏感:
json:"UserName"不会匹配{"username": "x"},除非前端也用驼峰
嵌套结构体或 map 字段想自定义键?Tag 只作用于直接字段
json Tag 无法穿透到嵌套结构体内部字段,也不能改变 map[string]interface{} 的 key 名 —— 它只控制当前结构体字段在 JSON 中的“外层键名”。
- 例如
User struct { Profile Profile `json:"profile"` },Profile内部字段仍按自身 Tag 或字段名序列化,Profile这个字段整体在 JSON 中叫"profile" - 若想统一调整嵌套字段命名风格(比如全转 snake_case),得手动遍历或用第三方库(如
mapstructure+ 自定义 decoder),Gin 和标准库都不提供全局配置 - map 类型如
Data map[string]string `json:"data"`,map 的 key 不受 Tag 影响,仍是原始字符串,Tag 只决定这个 map 整体在 JSON 中叫什么
真正麻烦的不是怎么写 Tag,而是搞清它在哪一层起作用、哪一层不起作用。字段导出性、omitempty 的语义边界、嵌套时的作用域,这三个地方错一个,JSON 就和预期对不上。


















