JSON 字段省略
Go 里给结构体字段加上 json:",omitempty" 后,零值结构体仍然出现在 JSON 中,通常不是标签失效,而是把“零值”和“空值”混成了一件事。对默认的 encoding/json 语义来说,omitempty 只识别 false、0、nil 指针、nil 接口,以及长度为 0 的数组、切片、map 和字符串;普通 struct 不在这份列表里,所以它会继续编码成对象。
官方地址:https://pkg.go.dev/encoding/json
- 结构体字段需要表达“没有这个对象”时,用指针并让零值为
nil。 - 字段需要表达“这个 Go 值是类型零值”时,在支持的 Go 版本中优先考虑
omitzero。 - 只写
omitempty,不能把普通零值结构体自动变成缺失字段。
先分清“空 JSON 值”和“Go 零值”
omitempty 的名字容易让人以为它会递归检查结构体内部的每个字段。实际上,默认 encoding/json 判断的是当前字段值本身是否属于空值集合。整数 0、布尔值 false、空字符串、空切片和空 map 都能触发省略;一个结构体即使所有成员都是零值,类型本身仍然是一个可编码的对象。

omitempty 检查字段外层值类型,零值结构体仍是对象;这是原创静态结构说明图,不是运行截图。最小复现:零值结构体为什么还在
下面的 Profile 没有任何有效内容,但 Account.Profile 不是 nil。调用 Marshal 时,它会被编码成一个空对象,而不是被删除:
package main
import (
"encoding/json"
"fmt"
)
type Profile struct {
City string `json:"city,omitempty"` // 空字符串只影响 Profile 内部的 city
}
type Account struct {
Name string `json:"name,omitempty"` // 空名称会被省略
Profile Profile `json:"profile,omitempty"` // struct 本身不会因零值被省略
}
func main() {
b, err := json.Marshal(Account{})
if err != nil {
// 序列化失败时保留错误,避免把空结果误当成成功。
panic(err)
}
fmt.Println(string(b)) // 结果为 {"profile":{}}
}
这里有两个层次:Profile.City 是字符串,空字符串符合 omitempty;Account.Profile 是结构体,结构体不符合默认空值判断。因此给内层字段继续加标签,不能改变外层结构体字段的省略结果。
需要“缺失对象”时改成指针
如果接口协议把“没有资料”与“资料存在但字段为空”区分开,指针通常是最直观的表达:nil 表示没有对象,非 nil 表示对象存在。此时 omitempty 检查的是指针,nil 指针可以被省略:
type Profile struct {
City string `json:"city,omitempty"` // 城市为空时省略 city
}
type Account struct {
Name string `json:"name,omitempty"` // 空名称不输出
Profile *Profile `json:"profile,omitempty"` // nil 表示没有 profile
}
func encodeAccount(withProfile bool) ([]byte, error) {
account := Account{}
if withProfile {
account.Profile = &Profile{City: "Shanghai"} // 只有业务确认存在时才创建对象
}
return json.Marshal(account)
}
Account{} 的结果不包含 profile;当 withProfile 为 true 时,结果会包含 profile 对象。这个方案适合“缺失”和“存在”有不同业务含义的接口,但调用方要接受指针判空,不能把它当成普通值字段随意读取。
需要“Go 零值”语义时考虑 omitzero
当前 Go 官方文档把 omitzero 定义为按 Go 类型的零值省略:如果类型提供 IsZero() bool,优先使用它;否则按该类型的零值判断。它解决的是“这个值是否为零”的问题,与 omitempty 的空 JSON 值判断不是同一套规则。
type Window struct {
Start int `json:"start"` // 起点为 0 仍然保留,便于观察整个 Window 的零值
End int `json:"end"` // 终点为 0 仍然保留
}
type Query struct {
Window Window `json:"window,omitzero"` // Window 为零值时省略整个字段
}
func marshalQuery() ([]byte, error) {
q := Query{}
return json.Marshal(q) // 在支持 omitzero 的 Go 版本中,零值 Window 不输出
}
如果项目仍需兼容不支持 omitzero 的 Go 版本,不要把它当成普通未知选项继续发布;应回到指针字段,或者在组装响应 DTO 时显式决定是否填充该字段。选型的关键不是标签更短,而是接口是否需要“缺失”这个状态。

omitempty 或 omitzero 的决策路径,是原创静态说明图,不是运行截图。按顺序排查标签没有生效
- 先看标签是否写在字段上。正确形式是
json:"profile,omitempty",选项之间用逗号;把omitempty写到类型定义外或写成普通字符串,都不会改变编码规则。 - 再看字段是否导出。字段名首字母小写时,
encoding/json默认不会把它作为可导出的结构体字段;这和omitempty无关。 - 再看省略的是哪一层。内层
City被省略,只能说明City为空,不能推出外层Profile也应消失。 - 最后确认调用方向。
omitempty影响的是编码,也就是Marshal;它不会让Unmarshal输入里的字段自动变成缺失,也不会删除已经存在的 Go 值。
type Payload struct {
profile string `json:"profile,omitempty"` // 未导出字段不会被 encoding/json 作为输出字段
Profile string `json:"profile,omitempty"` // 导出字段才会参与 JSON 编码
}
func marshalPayload() ([]byte, error) {
return json.Marshal(Payload{profile: "hidden", Profile: "shown"})
// 结果只会包含 Profile 对应的 profile 键。
}
用一张表固定选择
| 想表达的语义 | 字段写法 | 零值时的结果 |
|---|---|---|
| 0、false、空字符串等空值不输出 | json:"x,omitempty" | 按默认空值集合省略 |
| 对象不存在 | *Detail `json:"detail,omitempty"` | nil 时省略 |
| 按 Go 类型零值省略 | Detail `json:"detail,omitzero"` | 零值对象时省略 |
| 始终不输出 | json:"-" | 与字段值无关,始终省略 |
常见问题
给结构体的每个成员都加 omitempty,外层对象会消失吗?
不会。内层标签只决定成员是否进入对象;外层对象是否省略,要看外层字段自身的类型和标签。
把结构体改成指针后,什么时候不该用 omitempty?
如果接口必须始终返回一个对象,即使对象内部为空,也不要用 omitempty;让指针保持非 nil,并明确返回空对象的契约。
omitzero 和 omitempty 能一起写吗?
官方文档允许同时指定,字段满足任一省略条件时都会被省略。但只有在项目的 Go 版本和接口兼容策略都允许时才这样做,避免让读者误以为两者语义完全相同。
所以,“JSON 字段省略”遇到零值结构体不生效时,先不要反复改标签。先问清楚字段需要表达的是“没有对象”、 “对象存在但内容为空”,还是“Go 类型零值”;分别用指针、保留对象,或 omitzero,问题就能落到可解释的接口设计上。
JSON 自定义解码
- 上一篇
- JSON 自定义解码
- 下一篇
- JSON 数字错误
-
- Golang · Go问答 | 16分钟前 | 标准库 · 时间处理 · Go问答 · Go 时间解析 time.Parse RFC3339Nano RFC3339 Nanosecond
- RFC3339 解析
- 414浏览 收藏
-
- Golang · Go问答 | 28分钟前 |
- JSON 自定义解码
- 132浏览 收藏
-
- Golang · Go问答 | 36分钟前 | 标准库 · go · url · net/url PathEscape URL转义 QueryEscape
- URL 转义
- 399浏览 收藏
-
- Golang · Go问答 | 39分钟前 | url · Go问答 · Go net/url QueryEscape ParseQuery QueryUnescape PathUnescape
- 查询参数解码
- 102浏览 收藏
-
- Golang · Go问答 | 42分钟前 | Go问答 · irrFuscator(代码混淆工具) Go net/http Last-Modified ServeContent If-Modified-Since ModTime
- 静态内容缓存
- 274浏览 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 543次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 516次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 500次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 485次学习
-
- PubMedQA
- 深入了解PubMedQA生物医学问答数据集,涵盖其核心功能、使用方法及在临床决策、药物研发等场景的应用,助力提升NLP模型性能。
- 982次使用
-
- H2O EvalGPT
- H2O EvalGPT是H2O.ai推出的开源LLM评估平台,提供详细的大模型性能排行榜、行业特定基准测试及A/B测试功能,助您快速选择最适合项目的高性能大语言模型。
- 986次使用
-
- LMArena
- LMArena是加州大学伯克利分校推出的AI模型匿名评测平台。通过盲测投票机制,用户可对比不同大模型回答并生成实时排行榜,助力开发者优化模型及用户选择最佳AI工具。
- 1105次使用
-
- HELM
- 深入了解斯坦福推出的HELM(Holistic Evaluation of Language Models)大模型评测体系。本文解析其核心功能、安装配置步骤及应用场景,涵盖准确性、公平性、鲁棒性等多维度指标,助力开发者全面优化语言模型性能。
- 1075次使用
-
- MMBench
- MMBench是由上海人工智能实验室等机构联合推出的多模态基准测试平台,提供细粒度能力评估、大规模数据集及VLMEvalKit工具。本文详细介绍其核心功能、安装使用方法及应用场景,助力开发者全面评估多模态模型性能。
- 916次使用
-
- 用Nginx反向代理部署go写的网站。
- 2023-01-17 502浏览
-
- GoLand调式动态执行代码
- 2023-01-13 502浏览
-
- Go types.Selection.Type 与 Obj.Type 为什么不总是相同
- 2026-11-10 501浏览
-
- Go flag.FlagSet ContinueOnError 怎么捕获帮助输出和解析错误
- 2026-11-05 501浏览
-
- Go netip.ParseAddr 为什么拒绝带端口字符串
- 2026-11-03 501浏览

