当前位置:首页 > 文章列表 > Golang > Go问答 > JSON 字段省略

JSON 字段省略

来源:17golang原创 2026-11-11 16:02:40 0浏览 收藏

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 都能触发省略;一个结构体即使所有成员都是零值,类型本身仍然是一个可编码的对象。

Go encoding/json omitempty 空值集合与零值结构体的关系说明图
图1:说明 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 时显式决定是否填充该字段。选型的关键不是标签更短,而是接口是否需要“缺失”这个状态。

Go JSON 字段省略在指针字段与 omitzero 之间的决策路径说明图
图2:从字段语义出发选择指针 omitempty 或 omitzero 的决策路径,是原创静态说明图,不是运行截图。

按顺序排查标签没有生效

  1. 先看标签是否写在字段上。正确形式是 json:"profile,omitempty",选项之间用逗号;把 omitempty 写到类型定义外或写成普通字符串,都不会改变编码规则。
  2. 再看字段是否导出。字段名首字母小写时,encoding/json 默认不会把它作为可导出的结构体字段;这和 omitempty 无关。
  3. 再看省略的是哪一层。内层 City 被省略,只能说明 City 为空,不能推出外层 Profile 也应消失。
  4. 最后确认调用方向。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,问题就能落到可解释的接口设计上。

版本声明
本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
JSON 自定义解码JSON 自定义解码
上一篇
JSON 自定义解码
JSON 数字错误
下一篇
JSON 数字错误
查看更多
最新文章
查看更多
课程推荐
  • 前端进阶之JavaScript设计模式
    前端进阶之JavaScript设计模式
    设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
    543次学习
  • GO语言核心编程课程
    GO语言核心编程课程
    本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
    516次学习
  • 简单聊聊mysql8与网络通信
    简单聊聊mysql8与网络通信
    如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
    500次学习
  • JavaScript正则表达式基础与实战
    JavaScript正则表达式基础与实战
    在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
    487次学习
  • 从零制作响应式网站—Grid布局
    从零制作响应式网站—Grid布局
    本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
    485次学习
查看更多
AI推荐
  • PubMedQA数据集详解:生物医学问答基准、功能与应用指南
    PubMedQA
    深入了解PubMedQA生物医学问答数据集,涵盖其核心功能、使用方法及在临床决策、药物研发等场景的应用,助力提升NLP模型性能。
    982次使用
  • H2O EvalGPT:开源LLM大模型评估与排行榜工具
    H2O EvalGPT
    H2O EvalGPT是H2O.ai推出的开源LLM评估平台,提供详细的大模型性能排行榜、行业特定基准测试及A/B测试功能,助您快速选择最适合项目的高性能大语言模型。
    986次使用
  • LMArena是什么?伯克利AI模型评估平台使用指南与功能解析
    LMArena
    LMArena是加州大学伯克利分校推出的AI模型匿名评测平台。通过盲测投票机制,用户可对比不同大模型回答并生成实时排行榜,助力开发者优化模型及用户选择最佳AI工具。
    1105次使用
  • 斯坦福HELM:大语言模型Holistic Evaluation整体评估框架详解
    HELM
    深入了解斯坦福推出的HELM(Holistic Evaluation of Language Models)大模型评测体系。本文解析其核心功能、安装配置步骤及应用场景,涵盖准确性、公平性、鲁棒性等多维度指标,助力开发者全面优化语言模型性能。
    1075次使用
  • MMBench详解:多模态大模型基准测试、功能特点与使用指南
    MMBench
    MMBench是由上海人工智能实验室等机构联合推出的多模态基准测试平台,提供细粒度能力评估、大规模数据集及VLMEvalKit工具。本文详细介绍其核心功能、安装使用方法及应用场景,助力开发者全面评估多模态模型性能。
    916次使用
微信登录更方便
  • 密码登录
  • 注册账号
登录即同意 用户协议 和 隐私政策
返回登录
  • 重置密码