您的位置:首页 > 编程语言 > Go语言

Go序列化嵌套结构体

2021-09-16 09:51 1271 查看

[toc]


1. 忽略某个字段

  1. 格式

    // 使用json tag指定json序列化与反序列化时的行为
    type Person struct {
    Name   string `json:"name"` // 指定json序列化/反序列化时使用小写name
    Age    int64
    Weight float64 `json:"-"` // 指定json序列化/反序列化时忽略此字段
    }
  2. 代码

    package main
    
    import (
    "encoding/json"
    "fmt"
    )
    
    type Person struct {
    Name string `json:"name"` // 指定json序列化/反序列化时使用小写name
    Age  int64
    Weight float64 `json:"-"` // 指定json序列化/反序列化时忽略此字段
    }
    
    func main() {
    person := Person{
    Name: "Jack",
    Age:  29,
    Weight: 53.2,
    }
    
    p, err := json.Marshal(person)
    if err != nil {
    fmt.Printf("json.Marshal failed, err:%v\n", err)
    return
    }
    fmt.Printf("str:%s\n", p)
    }
  3. 输出

    str:{"name":"Jack","Age":29}

2. 忽略空值字段

struct
中的字段没有值时,
json.Marshal()
序列化的时候不会忽略这些字段,而是默认输出字段的类型零值(例如int和float类型零值是 0,string类型零值是"",对象类型零值是 nil)。


如果想要在序列序列化时忽略这些没有值的字段时,可以在对应字段添加

omitempty tag

  1. 示例

    package main
    
    import (
    "encoding/json"
    "fmt"
    )
    
    type User struct {
    Name  string   `json:"name"`
    Email string   `json:"email"`
    Hobby []string `json:"hobby"`
    }
    
    func omitemptyDemo() {
    // 当 struct 中的字段没有值Email,Hobby 字段时
    u1 := User{
    Name: "Jack",
    }
    
    // struct -> json string
    b, err := json.Marshal(u1)
    if err != nil {
    fmt.Printf("json.Marshal failed, err:%v\n", err)
    return
    }
    fmt.Printf("str:%s\n", b)
    }
    
    func main() {
    omitemptyDemo()
    }
  2. 输出

    // 结构体没有字段时,会使用结构体字段对应的类型默认值
    str:{"name":"Jack","email":"","hobby":null}

3. 去掉结构体没有字段的值

如果想要在最终的序列化结果中去掉空值字段,可以像下面这样定义结构体:

在对应字段处使用

json, omitempty tag
格式忽略不存在的字段值

  1. 格式

    // 在tag中添加omitempty忽略空值
    // 注意这里 hobby,omitempty 合起来是json tag值,中间用英文逗号分隔
    type User struct {
    Name  string   `json:"name"`
    Email string   `json:"email,omitempty"`
    Hobby []string `json:"hobby,omitempty"`
    }
  2. demo

    package main
    
    import (
    "encoding/json"
    "fmt"
    )
    
    type User2 struct {
    Name  string   `json:"name"`
    Email string   `json:"email,omitempty"`
    Hobby []string `json:"hobby,omitempty"`
    }
    
    func omitemptyDemo1() {
    // 当 struct 中的字段没有值Email,Hobby 字段时
    u1 := User2{
    Name: "Jack",
    }
    
    // struct -> json string
    b, err := json.Marshal(u1)
    if err != nil {
    fmt.Printf("json.Marshal failed, err:%v\n", err)
    return
    }
    fmt.Printf("str:%s\n", b)
    }
    
    func main() {
    omitemptyDemo1()
    }
  3. 输出

    // 输出会将空值忽略掉
    str:{"name":"Jack"}  // 序列化结果中没有email和hobby字段

4. 忽略嵌套结构体空值字段


4.1 解套结构体解包序列化

所谓嵌套解包,就是将嵌套的字段以水平展开与原结构体字段合并成一个无嵌套的 json字符串

  1. demo结构如下

    type Profile struct {
    Website string `json:"site"`
    Slogan  string `json:"slogan"`
    }
    
    type User struct {
    Name  string   `json:"name"`
    Email string   `json:"email,omitempty"`
    Hobby []string `json:"hobby,omitempty"`
    Profile             //匿名结构体继承
    }
    
    # 嵌套结构体序列化
    func nestedStructDemo() {
    // 实例化结构体,缺少嵌套结构体 Profile
    u1 := User{
    Name:  "左右逢源",
    Hobby: []string{"足球", "双色球"},
    }
    
    // 将结构体序列化成 json字符串
    b, err := json.Marshal(u1)
    if err != nil {
    fmt.Printf("json.Marshal failed, err:%v\n", err)
    return
    }
    fmt.Printf("str:%s\n", b)
    }
  2. 匿名嵌套

    Profile
    时序列化后的json串为单层的, 这种序列化会将 嵌套结构体 解包,如下

    str:{"name":"左右逢源","hobby":["足球","双色球"],"site":"","slogan":""}

4.2 想要变成嵌套的json串,需要改为具名嵌套或定义字段tag

  1. 上面的结果并不是我们想要的结果,我们想要的json格式应该如下

    str:{"name":"左右逢源","hobby":["足球","双色球"],"profile":{"site":"","slogan":""}}
  2. 嵌套格式如下

    type User struct {
    Name    string   `json:"name"`
    Email   string   `json:"email,omitempty"`
    Hobby   []string `json:"hobby,omitempty"`
    Profile `json:"profile"`    //嵌套结构体,加上tag
    }
    //str:{"name":"左右逢源","hobby":["足球","双色球"],"profile":{"site":"","slogan":""}}

4.3 嵌套结构体如果无值,将对应json字段忽略掉

  1. 有的时候我们想在序列化的时候,如果嵌套结构体没有值,那么序列化成json的字段时将该字段忽略掉,这样可以节省内存空间,同时对于api返回也可以防止不必要的返回

    str:{"name":"左右逢源","hobby":["足球","双色球"]} // profile结构体为空时,将此字段忽略掉
  2. 格式如下

    type User struct {
    Name     string   `json:"name"`
    Email    string   `json:"email,omitempty"`
    Hobby    []string `json:"hobby,omitempty"`
    *Profile `json:"profile,omitempty"`   // 使用结构体指针,同时tag加入 omitempty属性表示可以为空
    }

4.4 不修改原结构体忽略空值字段

  1. 比如这样的场景, 用户在调用创建用户接口时,api返回调用结果,需要json序列化User,但是不想把密码也序列化,又不想修改User结构体,这个时就可以使用创建另外一个结构体PublicUser匿名嵌套原User,同时指定Password字段为匿名结构体指针类型,并添加omitemptytag,示例代码如下:

    type User struct {
    Name     string `json:"name"`
    Password string `json:"password"`
    }
    
    type PublicUser struct {
    *User             // 匿名嵌套
    // 这里相当于将原User结构体中的Password字段覆盖掉了,使用空结构体及tag omitempty强制忽略这个字段
    Password *struct{} `json:"password,omitempty"`
    }
    
    func omitPasswordDemo() {
    u1 := User{
    Name:     "左右逢源",
    Password: "123456",
    }
    
    b, err := json.Marshal(PublicUser{User: &u1})
    if err != nil {
    fmt.Printf("json.Marshal u1 failed, err:%v\n", err)
    return
    }
    
    fmt.Printf("str:%s\n", b)  //str:{"name":"左右逢源"}}
  2. 输出的时候就只有 Name字段被序列化成了 json字符串

    str:{"name":"左右逢源"}

5. 参考

  1. https://zhuanlan.zhihu.com/p/296248435
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: