Golang中omitempty关键字的具体实现

 更新时间:2022年01月17日 10:17:46   作者:Old_Panda  
本文主要介绍了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
}

P.S. 本文中拿来作示例的地址是旧金山亚洲艺术博物馆的地址,藏品丰富,上到夏商周,下至明清的文物都能看到,几年前第一次去参观,很是喜欢,印象深刻。

到此这篇关于Golang中omitempty关键字的具体实现的文章就介绍到这了,更多相关Golang omitempty 内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • go语言中布隆过滤器低空间成本判断元素是否存在方式

    go语言中布隆过滤器低空间成本判断元素是否存在方式

    这篇文章主要为大家介绍了go语言中布隆过滤器低空间成本判断元素是否存在方式详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-09-09
  • 使用Go Validator有效验证数据示例分析

    使用Go Validator有效验证数据示例分析

    作为一名开发者,确保Go应用中处理的数据是有效和准确的非常重要,Go Validator是一个开源的数据验证库,为Go结构体提供强大且易于使用的数据验证功能,本篇文章将介绍Go Validator库的主要特点以及如何在Go应用中使用它来有效验证数据
    2023-12-12
  • Golang实现按比例切分流量的示例详解

    Golang实现按比例切分流量的示例详解

    我们在进行灰度发布时,往往需要转发一部分流量到新上线的服务上,进行小规模的验证,随着功能的不断完善,我们也会逐渐增加转发的流量,这就需要按比例去切分流量,那么如何实现流量切分呢,接下来小编就给大家详细的介绍一下实现方法,需要的朋友可以参考下
    2023-09-09
  • Golang 在gin框架中如何使用JWT鉴权

    Golang 在gin框架中如何使用JWT鉴权

    JWT,全称 JSON Web Token,是一种开放标准(RFC 7519),用于安全地在双方之间传递信息,这篇文章主要介绍了golang 在Gin框架中使用JWT鉴权,需要的朋友可以参考下
    2024-07-07
  • 如何用Go判断元素是否在切片中

    如何用Go判断元素是否在切片中

    切片(Slice)是一个拥有相同类型元素的可变长度的序列,下面这篇文章主要给大家介绍了关于如何用Go判断元素是否在切片中的相关资料,文中通过实例代码介绍的非常详细,需要的朋友可以参考下
    2022-06-06
  • Go语言之fo循环与条件判断

    Go语言之fo循环与条件判断

    这篇文章主要介绍Go语言之fo循环与条件判断,for 循环是一个循环控制结构,可以执行指定次数的循环。下面文章我们结合条件判断,感兴趣的朋友可以参考一下
    2021-10-10
  • 优雅管理Go Project生命周期

    优雅管理Go Project生命周期

    这篇文章主要为大家介绍了如何优雅的管理Go Project生命周期,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-03-03
  • Go语言编程中字符串切割方法小结

    Go语言编程中字符串切割方法小结

    这篇文章主要介绍了Go语言编程中字符串切割方法小结,所整理的方法都来自字符串相关的strings包,需要的朋友可以参考下
    2015-10-10
  • Go素数筛选分析详解

    Go素数筛选分析详解

    学习Go语言的过程中,遇到素数筛选的问题。这是一个经典的并发编程问题,是某大佬的代码,短短几行代码就实现了素数筛选,这篇文章主要介绍了Go素数筛选分析,需要的朋友可以参考下
    2022-10-10
  • Go语言七篇入门教程一简介初识

    Go语言七篇入门教程一简介初识

    本篇是Go语言七篇入门系列第一篇Go语言初识及简单介绍,从现在开始一起打开Go语言的学习大门吧,希望能够有所帮助,祝大家多多进步
    2021-11-11

最新评论