GO 配置omitempty在JSON中忽略该字段

本文详细介绍了在 Golang 中使用 JSON 标签 `omitempty` 的方法与注意事项,包括如何避免序列化空字段,以及如何正确处理嵌套结构体和指针类型的特殊情况。

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

原文载于 https://old-panda.com/2019/12/11/golang-omitempty/

用法

熟悉 Golang 的朋友对于 json 和 struct 之间的转换一定不陌生,为了将代码中的结构体与 json 数据解耦,通常我们会在结构体的 field 类型后加上解释说明,例如在表示一个地址的时候, json 数据如下所示

{
“street”: “200 Larkin St”,
“city”: “San Francisco”,
“state”: “CA”,
“zipcode”: “94102”
}
与之相对应的 Golang 结构体表示可能是这个样子的

type address struct {
Street string json:"street" // 街道
Ste string json:"suite" // 单元(可以不存在)
City string json:"city" // 城市
State string json:"state" // 州/省
Zipcode string json:"zipcode" // 邮编
}
这样无论代码中的变量如何改变,我们都能成功将 json 数据解析出来,获得正确的街道,城市等信息,到目前为止一切正常。但如果我们想要将地址结构体恢复成 json 格式时,问题就来了。比方说我们用下面这段代码读取了地址 json ,然后根据业务逻辑处理了之后恢复成正常的 json 打印出来

func main() {
        data := `{
        "street": "200 Larkin St",
        "city": "San Francisco",
        "state": "CA",
        "zipcode": "94102"
    }`
    addr := new(address)
    json.Unmarshal([]byte(data), &addr)
 
        // 处理了一番 addr 变量...
 
    addressBytes, _ := json.MarshalIndent(addr, "", "    ")
    fmt.Printf("%s\n", string(addressBytes))
}

这段代码的输出是

{
“street”: “200 Larkin St”,
“suite”: “”,
“city”: “San Francisco”,
“state”: “CA”,
“zipcode”: “94102”
}
多了一行 “suite”: “”, ,而这则信息在原本的 json 数据中是没有的(在美国的地址中,如果不是群租公寓或者共享办公楼, suite 这一条不存在很正常,人们直接用街道门牌号来表示地址就足够了),但我们更希望的是,在一个地址有 suite 号码的时候输出,不存在 suite 的时候就不输出,幸运的是,我们可以在 Golang 的结构体定义中添加 omitempty 关键字,来表示这条信息如果没有提供,在序列化成 json 的时候就不要包含其默认值。稍作修改,地址结构体就变成了

type address struct {
Street string json:"street"
Ste string json:"suite,omitempty"
City string json:"city"
State string json:"state"
Zipcode string json:"zipcode"
}
重新运行,即可得到正确的结果。

陷阱

带来方便的同时,使用 omitempty 也有些小陷阱,一个是该关键字无法忽略掉嵌套结构体。还是拿地址类型说事,这回我们想要往地址结构体中加一个新 field 来表示经纬度,如果没有缺乏相关的数据,暂时可以忽略。新的 struct 定义如下所示

type address struct {
    Street     string     `json:"street"`
    Ste        string     `json:"suite,omitempty"`
    City       string     `json:"city"`
    State      string     `json:"state"`
    Zipcode    string     `json:"zipcode"`
    Coordinate coordinate `json:"coordinate,omitempty"`
}
 
type coordinate struct {
    Lat float64 `json:"latitude"`
    Lng float64 `json:"longitude"`
}

读入原来的地址数据,处理后序列化输出,我们就会发现即使加上了 omitempty 关键字,输出的 json 还是带上了一个空的坐标信息

{
“street”: “200 Larkin St”,
“city”: “San Francisco”,
“state”: “CA”,
“zipcode”: “94102”,
“coordinate”: {
“latitude”: 0,
“longitude”: 0
}
}
为了达到我们想要的效果,可以把坐标定义为指针类型,这样 Golang 就能知道一个指针的“空值”是多少了,否则面对一个我们自定义的结构, Golang 是猜不出我们想要的空值的。于是有了如下的结构体定义

type address struct {
    Street     string      `json:"street"`
    Ste        string      `json:"suite,omitempty"`
    City       string      `json:"city"`
    State      string      `json:"state"`
    Zipcode    string      `json:"zipcode"`
    Coordinate *coordinate `json:"coordinate,omitempty"`
}
 
type coordinate struct {
    Lat float64 `json:"latitude"`
    Lng float64 `json:"longitude"`
}

相应的输出为

{
“street”: “200 Larkin St”,
“city”: “San Francisco”,
“state”: “CA”,
“zipcode”: “94102”
}

另一个“陷阱”

对于用 omitempty 定义的 field ,如果给它赋的值恰好等于默认空值的话,在转为 json 之后也不会输出这个 field 。比如说上面定义的经纬度坐标结构体,如果我们将经纬度两个 field 都加上 omitempty

type coordinate struct {
Lat float64 json:"latitude,omitempty"
Lng float64 json:"longitude,omitempty"
}
然后我们对非洲几内亚湾的“原点坐标”非常感兴趣,于是编写了如下代码

func main() {
    cData := `{
        "latitude": 0.0,
        "longitude": 0.0
    }`
    c := new(coordinate)
    json.Unmarshal([]byte(cData), &c)
 
        // 具体处理逻辑...
 
    coordinateBytes, _ := json.MarshalIndent(c, "", "    ")
    fmt.Printf("%s\n", string(coordinateBytes))
}

最终我们得到了一个

{}
这个坐标消失不见了!但我们的设想是,如果一个地点没有经纬度信息,则悬空,这没有问题,但对于“原点坐标”,我们在确切知道它的经纬度的情况下,(0.0, 0.0)仍然被忽略了。正确的写法也是将结构体内的定义改为指针

type coordinate struct {
Lat *float64 json:"latitude,omitempty"
Lng *float64 json:"longitude,omitempty"
}
这样空值就从 float64 的 0.0 变为了指针类型的 nil ,我们就能看到正确的经纬度输出。

{
“latitude”: 0,
“longitude”: 0
}
原文连接:https://old-panda.com/2019/12/11/golang-omitempty/

补充:

此时没有添加"omitempty"关键字,反序列化的时候,如果结构体中Name为空字符串的时候,结果中不包含

//定义一个结构体
type Monster struct {
	Name string `json:"name"`
	Age int
	Birthday string
	Sal float64
	Skill string
}
 
func testStruct() {
	//演示
	monster := Monster{
		Name : "",//空字符串
		Age : 500,
		Birthday : "2011-11-11",
		Sal : 8000.0,
		Skill : "牛魔拳",
	}
	//将 monster 序列化
	data, err := json.Marshal(&monster)
	if err != nil {
		fmt.Printf("序列号错误 err=%v\n", err)
	}
	//输出序列化后的结果
	fmt.Printf("monster 序列化后=%v\n", string(data))
	//反序列化
	monster2 := &Monster{}
	json.Unmarshal(data,monster2)
	fmt.Printf("反序列后的结果:%v",monster2)
}
 
func main() {
	testStruct()
}

结果:
monster 序列化后={“name”:“”,“Age”:500,“Birthday”:“2011-11-11”,“Sal”:8000,“Skill”:“牛魔拳”}
反序列后的结果:&{ 500 2011-11-11 8000 牛魔拳}

<think>好的,我现在需要回答用户在go-zero框架中结构体字段标记`: "info,omitempty"`的作用,包括含义、使用场景以及结构体序列化相关的内容。首先,我应该回忆一下Go语言中结构体标签的基本知识,然后再结合go-zero框架的特定用法来分析。 首先,Go语言的结构体标签(struct tags)通常是键值对的形式,用于在运行时通过反射获取元数据。常见的比如json标签,例如`json:"name,omitempty"`,这里的`omitempty`作用是在序列化时如果该字段为零值,则忽略字段。 用户提到的标签是`: "info,omitempty"`,这里的键看起来是`info`,而`omitempty`是选项。可能这个标签是go-zero框架中自定义的,用于某些特定的处理,比如生成API文档、验证或者序列化等。需要结合go-zero的文档或已知的使用场景来分析。 根据用户提供的引用,特别是引用[3]中提到了结构体中的缓存项和序列化协议,引用[2]提到了API服务的代码实战,引用[4]关于命名准则的信息可能帮助不大,但引用[1]提到了syntax语法声明中的info语法块。可能info在go-zero中有特定的用途,比如定义API的信息或者字段的元数据。 在go-zero框架中,结构体常用于定义API请求和响应的模型,通常使用`tag`来指定字段的序列化名称和规则。例如,在定义HTTP服务时,可能使用框架特定的标签来处理请求参数的绑定,比如`form:"name"`或`json:"name"`。但用户的问题中的标签是`info`,可能属于go-zero的自定义标签,用于生成文档或者其他处理。 接下来,分析`omitempty`的作用,这在JSON序列化中常见,表示当字段为空值时,不包含该字段。可能`info`标签用于某种特定的序列化或文档生成,结合`omitempty`来控制字段的输出。 使用场景可能是在定义API模型时,通过`info`标签指定字段的信息,比如在生成Swagger文档时显示描述,同时`omitempty`确保在字段为空时不包含该字段,减少不必要的数据传输。 此外,可能`info`标签在go-zero中用于验证或其他中间件处理,例如定义字段的说明信息,或者用于配置生成API文档时的元数据。例如,在go-zero的API定义中,可能通过结构体标签来指定字段的示例值、描述等,类似于OpenAPI的扩展属性。 需要验证这些推测是否正确。可能参考go-zero的官方文档或者示例代码。例如,在go-zero的RESTful API开发中,定义服务时使用`service`语法块,结构体的标签可能用于请求参数的绑定和验证。比如,使用`json`标签处理JSON序列化,`path`标签处理路径参数,`form`处理表单参数,而`info`可能是一个自定义标签,用于其他用途,比如生成文档时的字段描述。 例如,假设有一个结构体定义如下: ```go type UserInfo struct { Name string `json:"name" info:"用户名,omitempty"` Age int `json:"age" info:"年龄,omitempty"` } ``` 这里的`info`标签可能用于生成API文档时显示每个字段的说明信息,而`omitempty`在这里可能表示如果该字段为空,则在文档中不显示该字段的说明,或者在其他处理中忽略字段。不过,这需要进一步确认。 另外,可能`info`标签是go-zero框架中某种特定组件的配置,比如在定义API路由时,结构体字段的标签被用于参数解析或验证,此时`info`可能对应某种验证规则或说明。 总结来说,用户的问题中的`: "info,omitempty"`结构体标签的含义可能包括: 1. `info`:指定字段的元数据,如描述信息,用于生成文档或API说明。 2. `omitempty`:在序列化或处理时,如果字段值为空,则忽略字段。 使用场景可能是在API模型定义中,结合go-zero的代码生成工具或中间件,自动处理字段的序列化和文档生成。例如,当使用go-zero的goctl工具生成API代码时,结构体标签中的`info`可能被用来填充Swagger文档中的字段描述,而`omitempty`确保在生成请求或响应示例时,空值字段被省略。 需要进一步确认这些标签的具体作用,可能需要查阅go-zero的文档或源码。但根据现有的引用和常规用法,可以做出以上合理的推断。</think>在go-zero框架中,结构体字段标记`: "info,omitempty"`的作用如下: ### 1. 标签含义 - **`info`**:表示该字段在特定场景下的元数据标识,常用于API文档生成或字段描述信息的声明[^2][^4]。类似于OpenAPI规范中的字段说明,可通过代码生成工具解析后展示在接口文档中。 - **`omitempty`**:通用选项,表示当字段值为零值(如空字符串、0、nil等)时,在序列化过程中自动忽略字段[^3]。 ### 2. 使用场景 - **API文档生成**:在定义API请求/响应模型时,通过`info`标签声明字段的业务含义或示例,配合goctl工具生成Swagger等格式的接口文档[^2]。 - **数据序列化控制**:结合`omitempty`选项,在JSON/YAML序列化时动态过滤空值字段,减少无效数据传输。例如: ```go type UserRequest struct { Name string `json:"name" info:"用户名,omitempty"` // 空值时序列化结果不包含该字段 Age int `json:"age" info:"年龄,omitempty"` } ``` ### 3. 结构体序列化示例 假设有以下结构体定义: ```go type Article struct { Title string `json:"title" info:"文章标题,omitempty"` Views int `json:"views" info:"阅读量,omitempty"` } ``` - 当`Views=0`时,序列化为JSON忽略字段: ```json {"title": "Go语言实战"} ``` ### 4. 扩展说明 - **标签优先级**:若同时存在多个标签(如`json`和`info`),不同框架组件可能按需解析特定标签。例如,`json`标签控制序列化字段名,`info`标签用于文档生成[^3][^4]。 - **代码生成集成**:通过goctl的`api`命令,可直接将`info`标签内容映射到生成的API文档中,提升开发效率[^1][^2]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值