omitempty标签必须显式添加才能跳过零值字段,go的json包默认不忽略空字符串、0或nil切片;字段需导出(首字母大写),且标签格式为json:"key,omitempty",空字符串""、整数0、nil切片等类型零值才被跳过。

结构体字段加 omitempty 标签才跳过空值
Go 的 json 包默认不会省略零值字段,哪怕字段是空字符串、0 或 nil 切片。想让 JSON 序列化时自动忽略这些字段,必须在结构体 tag 里显式加上 omitempty。
常见误区是只写 json:"name",结果 Name 字段值为空字符串时仍会输出 "name": ""。正确写法是:
type User struct {
ID int64 `json:"id"`
Name string `json:"name,omitempty"`
Age int `json:"age,omitempty"`
Tags []string `json:"tags,omitempty"`
}
-
omitempty对字符串生效:空字符串""被跳过 - 对数字生效:值为
0(int/float)被跳过 - 对切片/映射生效:nil 或长度为 0 的
[]string{}都被跳过(注意:两者行为一致) - 对指针/接口生效:nil 值被跳过
空切片输出 [] 而不是 null 的关键在初始化
前端常要求“没数据也返回空数组”,但 Gin 默认把 nil 切片序列化成 null。这不是 Gin 的问题,而是 Go json 包的规则:nil slice → null,空 slice([]T{} 或 make([]T, 0))→ []。
所以不能依赖数据库 ORM 自动初始化切片字段。比如:
type Response struct {
Data []User `json:"data"`
}
如果 response.Data 是 nil(比如查询没结果且未赋值),c.JSON(200, response) 就会输出 "data": null。
解决方法只有两个:
- 在赋值前主动初始化:
response.Data = make([]User, 0)或response.Data = []User{} - 或在构建响应时内联处理:
c.JSON(200, gin.H{"data": response.Data})改成c.JSON(200, gin.H{"data": response.Data})→ 不行,得先判空;正确写法是:c.JSON(200, gin.H{"data": response.Data})替换为c.JSON(200, gin.H{"data": response.Data})?不,直接用三元逻辑更稳妥:"data": func() interface{} { if response.Data == nil { return []User{} } else { return response.Data } }()——但太绕,推荐初始化
c.ShouldBindJSON 不校验空字段,binding:"required" 才管
很多人以为加了 omitempty 就能“同时让绑定和输出都忽略空值”,其实完全无关。omitempty 只影响 c.JSON() 输出;而请求入参是否允许为空,由 binding tag 决定。
例如:
type CreateUserReq struct {
Name string `json:"name" binding:"required"`
Email string `json:"email" binding:"required,email"`
Avatar string `json:"avatar,omitempty"` // 输出时可空,但绑定时照样接收空字符串
}
- 前端传
{"name":"a","email":"b","avatar":""}→ 绑定成功,Avatar被设为空字符串,omitempty让它不出现在响应里 - 前端传
{"name":"a","email":"b"}→Avatar字段根本不会被赋值(Go 结构体字段保持零值),所以响应里也不出现 - 但
binding:"required"不会对Avatar生效,因为它没加这个 tag
字段首字母小写会导致 JSON 完全丢失
这是最隐蔽也最常踩的坑:结构体字段名首字母小写 → 非导出字段 → json 包直接无视,无论有没有 omitempty 或 json: tag。
比如:
type User struct {
id int64 `json:"id"` // ❌ 小写 id → 不会出现在 JSON 中
Name string `json:"name"` // ✅ 大写 Name → 正常输出
}
即使你写了 json:"id",只要字段名是小写 id,Go 就不会导出它,json.Marshal 完全看不到这个字段。
正确写法只有:
type User struct {
ID int64 `json:"id"` // ✅ 首字母大写
Name string `json:"name"`
}
顺带一提:json:"id,string" 这种写法只对数字类型有效,用于转成字符串输出,但它不解决字段不可见的问题——字段本身还得先导出。











