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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM