深度思维者

永远年轻,永远热泪盈眶

Go序列化嵌套结构体


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
♥永远年轻,永远热泪盈眶♥
posted @ 2021-09-16 09:51  failymao  阅读(52)  评论(0编辑  收藏  举报
Fork me on GitHub