1. 程式人生 > >golang omitempty 總結

golang omitempty 總結

## golang omitempty 總結 在使用Golang的時候,不免會使用Json和結構體的相互轉換,這時候常用的就是 **json.Marshal**和**json.Unmarshal**兩個函式。 這時候在定義json結構體的時候,我們會用到**omitempty**這個欄位,這個欄位看似簡單,但是卻有很多小坑,這篇文章帶你稍微研究一下他的用途和功能 ### Basic Usage 當我們設定json的struct的時候,會定義每個欄位對一個json的格式,比如定義一個dog 結構體: ``` type Dog struct { Breed string WeightKg int } ``` 現在我們對他進行初始化,將其編碼為JSON格式: ``` func main() { d := Dog{ Breed: "dalmation", WeightKg: 45, } b, _ := json.Marshal(d) fmt.Println(string(b)) } ``` 則輸出的結果為:**{"Breed":"dalmation","WeightKg":45}**,你可[點選這裡](https://play.golang.org/p/PPm7C9MHby-). 現在假如有一個結構體變數我們沒初始化,那麼結果可能也會跟我們預期的不太一樣: ``` func main() { d := Dog{ Breed: "pug", } b, _ := json.Marshal(d) fmt.Println(string(b)) } ``` 輸出的結果為:**{"Breed":"pug","WeightKg":0}**,明顯dog的weight是未知,而不是0,並不是我們想要的結果,我們更想要的結果是:**"WeightKg":null**。 為了實現這樣的目的,我們這時候應該使用omitempty 變數來幫我們實現,當我們在Dog結構體加上這個tag的時候: ``` type Dog struct { Breed string // The first comma below is to separate the name tag from the omitempty tag WeightKg int `json:",omitempty"` } ``` 輸出結果為:**{"Breed":"pug"}**。現在**WeightKg**就被設定為預設零值(對於int應該為0,對於string應該為"", 指標的話應該為nil)。 ### 不能單純使用omitted 當結構體相互巢狀的時候,那麼omitempty就可能出現問題,比如: ``` type dimension struct { Height int Width int } type Dog struct { Breed string WeightKg int Size dimension `json:",omitempty"` } func main() { d := Dog{ Breed: "pug", } b, _ := json.Marshal(d) fmt.Println(string(b)) } ``` 輸出結果為: ``` {"Breed":"pug","WeightKg":0,"Size":{"Height":0,"Width":0}} ``` 我們已經使用omitempty標註的dimension還是顯示了出來。這是因為結構體dimension不知道空值是什麼,GO只知道簡單結構體例如int,string,pointer 這種型別的空值,為了不顯示我們沒有提供值的自定義結構體,我們可以使用結構體指標: ``` type Dog struct { Breed string WeightKg int // Now `Size` is a pointer to a `dimension` instance Size *dimension `json:",omitempty"` } ``` 執行結果為: ``` {"Breed":"pug","WeightKg":0} ``` 為什麼會這樣呢?因為指標是基本型別啊,Golang知道他的空值是啥,所以就直接賦值為nil(指標型別的空值)。 現在出一個問題,下面的程式的初始結果是什麼? ``` type Dog struct { Age *int `json:",omitempty"` } func main() { age := 0 d := Dog{ Age: &age, } b, _ := json.Marshal(d) fmt.Println(string(b)) } ``` - A. {"Age":0} - B. {} 答案在最後揭曉。 ### 0, "", nil 三者的區別 現在很難的很區別的是:零值,值為0,預設值。 比如我們設定一個restaurant結構體: ``` type Restaurant struct { NumberOfCustomers int `json:",omitempty"` } func main() { d := Restaurant{ NumberOfCustomers: 0, } b, _ := json.Marshal(d) fmt.Println(string(b)) } ``` 他的輸出結果為: ``` {} ``` 這肯定不是我們想要的結果,因為這個時候我們想要他輸出0,他卻沒有任何值輸出。因為Golang把0當成了零值,所以跟沒有賦值是一樣的,比如: ``` type Restaurant struct { NumberOfCustomers int `json:",omitempty"` Name string } func main() { d := Restaurant{ //給NumberOfCustomers賦值 NumberOfCustomers: 0, Name: "hello", } b, _ := json.Marshal(d) fmt.Println(string(b)) } ``` 和 ``` type Restaurant struct { NumberOfCustomers int `json:",omitempty"` Name string } func main() { d := Restaurant{ //未給NumberOfCustomers賦值 Name: "hello", } b, _ := json.Marshal(d) fmt.Println(string(b)) } ``` 二者結果都是:**{"Name":"hello"}** 解決這樣的問題的一種方法是使用**int指標**,因為int指標的空值為nil,當我想輸出0的時候,我傳進去地址,地址肯定不是空值nil,這樣肯定會顯示出來0. ``` type Restaurant struct { NumberOfCustomers *int `json:",omitempty"` } func main() { d1 := Restaurant{} b, _ := json.Marshal(d1) fmt.Println(string(b)) //Prints: {} n := 0 d2 := Restaurant{ NumberOfCustomers: &n, } b, _ = json.Marshal(d2) fmt.Println(string(b)) //Prints: {"NumberOfCustomers":0} } ``` 基於上,我們應該謹慎使用**omitempty**,如果選擇使用了他,那你就要第一時間知道,各個值的空值是什麼?當我沒有給某個變數賦值的時候,他應該是什麼樣的,我想要什麼的輸出?這都是你要仔細斟酌的。 > 好了,現在公佈答案:A:因為他是int型別的指標,我們傳進去的也是指標,所以不會有任何問題。同時&age不是指標的nil值,所以不會被忽略,顯示的時候不會有問題,就是0. > ref: https://www.sohamkamani.com/golang/2018-07-19-golang-omitempty/; https://golang.org/pkg/encodi