Go语言的omitempty选项

文章介绍了Golang中json结构体标签omitempty的使用,包括它如何在字段为空时省略字段,以及在处理嵌套结构体时可能出现的问题。当结构体嵌套时,需要使用指针并为嵌套结构体的每个字段添加omitempty才能达到预期效果。对于需要区分空值有无实际意义的情况,使用指针定义变量是解决办法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >


在使用Golang的时候,不免会使用Json和结构体的相互转换,这时候常用的就是 json.Marshaljson.Unmarshal 两个函数。这时候在定义json结构体的时候,我们会用到 omitempty 这个字段,这个字段作用就是 空值省略,看似简单,但是却有很多小坑,这篇文章带你稍微研究一下他的用途和功能。

omit:

v. 删除; 忽略; 漏掉; 遗漏; 不做; 未能做;

adj. 省略了的;省去的;

基本用法

当我们设置json的struct的时候,会定义每个字段对一个json的格式,比如定义一个dog 结构体:

type Dog struct {
	Name   string
	Weight int
}

现在我们对他进行初始化,将其编码为JSON格式:

func main() {
	d := Dog{
		Name:   "小黑",
		Weight: 20,
	}
	marshalDog, err := json.Marshal(d)
	if err != nil {
		fmt.Println(err.Error())
	}
	fmt.Println(string(marshalDog))
}

则输出的结果为:

{"Name":"小黑","Weight":20}

现在假如有一个结构体变量我们没初始化,那么结果可能也会跟我们预期的不太一样:

d := Dog{
	Name: "小黑",
}

输出的结果为:

{"Name":"小黑","Weight":0}

很明显,狗的weight是未知,而不是0,并不是我们想要的结果,我们更想要的结果是:

{"Name":"小黑"}

为了实现这样的目的,我们这时候应该使用 omitempty 选项来帮我们实现,当我们在Dog结构体加上这个tag的时候:

type Dog struct {
	Name   string
	Weight int `json:",omitempty"`
}

此时,输出结果为:

{"Name":"小黑"}

不能单纯使用omitempty

当结构体相互嵌套的时候,那么omitempty就可能出现问题,比如:

type Variety struct {
	Color    string // 小狗的颜色(花色、黑色...)
	Category string // 小狗的品种(中华田园犬、哈士奇...)
}

type Dog struct {
	Name    string
	Weight  int     `json:",omitempty"`
	Variety Variety `json:",omitempty"` // 小狗的种类
}

func main() {
	d := Dog{
		Name: "小黑",
	}
	marshalDog, err := json.Marshal(d)
	if err != nil {
		fmt.Println(err.Error())
	}
	fmt.Println(string(marshalDog))
}

输出结果为:

{"Name":"小黑","Variety":{"Color":"","Category":""}}

omitempty 为什么对嵌套结构体不生效呢?这是因为GO只知道简单数据类型例如bool,int,string,pointer 这种类型的空值,不知道Variety这种自定义结构体类型的空值是什么,为了不显示我们没有提供值的自定义结构体,我们可以使用结构体指针:

type Dog struct {
	Name    string
	Weight  int      `json:",omitempty"`
	Variety *Variety `json:",omitempty"`
}

运行结果为:

{"Name":"小黑"}

但是如果给出下面情况的赋值:

d := Dog{
	Name:    "小黑",
	Variety: &Variety{Color: "black"},
}

运行结果为:

{"Name":"小黑","Variety":{"Color":"black","Category":""}}

可以看到,omitempty只对*Variety生效。所以想要嵌套结构体里面的字段也能有空值省略的效果,就要在定义嵌套的结构体的时候,对里面的每个字段都要加上omitempty选项。如下所示:

type Variety struct {
	Color    string `json:",omitempty"`
	Category string `json:",omitempty"`
}

运行结果如下:

{"Name":"小黑","Variety":{"Color":"black"}}

剩下的最后一个坑就是:如果想要某个值就是为空(有实际意义),也就是说当一个空值有实际意义的时候就显示,没有实际意义(当时还不知道其值)的时候就不显示,这样的需求应该怎么做?如果还是向上面那样,当我们需要Category为空的时候(Category:“”),最后会发现omitempty选项会把这个空值字段省略。正确的做法是使用指针来定义其变量。最后代码如下:

package main

import (
	"encoding/json"
	"fmt"
)

type Variety struct {
	Color    *string `json:",omitempty"`
	Category *string `json:",omitempty"` //一定要是*string类型
}

type Dog struct {
	Name    string
	Weight  int      `json:",omitempty"`
	Variety *Variety `json:",omitempty"`
}

func main() {
	// 小狗1测试:
	color := "black"
	//此处的空值有实际意义(尽管这个例子举的不太好,但假设此时的category空值就是一个有实际意义的值)
	category := ""
	dog1 := Dog{
		Name:    "小黑",
		Variety: &Variety{Color: &color, Category: &category},
	}
	marshalDog1, err := json.Marshal(dog1)
	if err != nil {
		fmt.Println(err.Error())
	}
	fmt.Println(string(marshalDog1))

	// 小狗2测试:
	color = "white"
	// 此时小狗的品种未知,希望序列化的时候不显示
	dog2 := Dog{
		Name:    "小白",
		Variety: &Variety{Color: &color},
	}
	marshalDog2, err := json.Marshal(dog2)
	if err != nil {
		fmt.Println(err.Error())
	}
	fmt.Println(string(marshalDog2))
}

运行结果如下:

{"Name":"小黑","Variety":{"Color":"black","Category":""}}
{"Name":"小白","Variety":{"Color":"white"}}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值