使用
熟悉 Golang 的朋友對於 tag、json 和 struct 都不陌生。
type Address struct {
City string `json:"city"`
Street string `json:"street"`
ZipCode string `json:"zip_code"`
}
func TestMarshal(t *testing.T) {
data := `{
"city": "Beijing",
"street": "a"
}`
addr := &Address{}
json.Unmarshal([]byte(data), addr)
fmt.Printf("%#v\n", addr)
addressBytes, _ := json.MarshalIndent(addr, "", " ")
fmt.Printf("%s\n", string(addressBytes))
}
/*
&omitempty.Address{City:"Beijing", Street:"a", ZipCode:""}
{
"city": "Beijing",
"street": "a",
"zip_code": ""
}
*/
我們可以看到,多了一行 "zip_code": "", ,而這則信息在原本的 json 數據中是沒有的,但我們更希望的是,在一個地址有 zip_code 號碼的時候輸出,不存在 zip_code 的時候就不輸出,幸運的是,我們可以在 Golang 的結構體定義中添加 omitempty 關鍵字,來表示這條信息如果沒有提供,在序列化成 json 的時候就不要包含其默認值。稍作修改,地址結構體就變成了
type Address struct {
City string `json:"city"`
Street string `json:"street"`
ZipCode string `json:"zip_code,omitempty"`
}
func TestMarshal(t *testing.T) {
data := `{
"city": "Beijing",
"street": "a"
}`
addr := &Address{}
json.Unmarshal([]byte(data), addr)
fmt.Printf("%#v\n", addr)
addressBytes, _ := json.MarshalIndent(addr, "", " ")
fmt.Printf("%s\n", string(addressBytes))
}
/*
&omitempty.Address{City:"Beijing", Street:"a", ZipCode:""}
{
"city": "Beijing",
"street": "a"
}
*/
成功解決。
陷阱
帶來方便的同時,使用 omitempty 也有些小陷阱。
陷阱一:關鍵字無法忽略掉嵌套結構體
還是拿地址類型說事,這回我們想要往地址結構體中加一個新的結構體來表示經緯度,如果沒有或缺乏相關的數據,可以忽略。新的 struct 定義如下所示
type Address struct {
City string `json:"city"`
Street string `json:"street"`
ZipCode string `json:"zip_code,omitempty"`
Coordinate coordinate `json:"coordinate,omitempty"`
}
type coordinate struct {
Lat float64 `json:"latitude"`
Lng float64 `json:"longitude"`
}
func TestMarshal(t *testing.T) {
data := `{
"city": "Beijing",
"street": "a"
}`
addr := &Address{}
json.Unmarshal([]byte(data), addr)
addressBytes, _ := json.MarshalIndent(addr, "", " ")
fmt.Printf("%s\n", string(addressBytes))
}
/*
{
"city": "Beijing",
"street": "a",
"coordinate": {
"latitude": 0,
"longitude": 0
}
}
*/
讀入原來的地址數據,處理后序列化輸出,我們就會發現即使對新的結構體字段加上了 omitempty 關鍵字,輸出的 json 還是帶上了一個空的坐標信息。
為了達到我們想要的效果,可以把坐標結構體定義為指針類型,這樣 Golang 就能知道一個指針的“空值”是多少了,否則面對一個我們自定義的結構, Golang 是猜不出我們想要的空值的。於是有了如下的結構體定義:
type Address struct {
City string `json:"city"`
Street string `json:"street"`
ZipCode string `json:"zip_code,omitempty"`
Coordinate *coordinate `json:"coordinate,omitempty"`
}
type coordinate struct {
Lat float64 `json:"latitude"`
Lng float64 `json:"longitude"`
}
// ... 同上 忽略
/*
{
"city": "Beijing",
"street": "a"
}
*/
2. 陷阱二:想要傳入零值
對於用 omitempty 定義的 字段 ,如果給它賦的值恰好等於默認空值的話,在轉為 json 之后也不會輸出這個 字段 。比如說上面定義的經緯度坐標結構體,如果我們將經緯度兩個 字段 都加上 omitempty
type coordinate struct {
Lat float64 `json:"latitude,omitempty"`
Lng float64 `json:"longitude,omitempty"`
}
func TestMarshal(t *testing.T) {
data := `{
"latitude": 1.0,
"longitude": 0.0
}`
c := &coordinate{}
json.Unmarshal([]byte(data), c)
fmt.Printf("%#v\n", c)
addressBytes, _ := json.MarshalIndent(c, "", " ")
fmt.Printf("%s\n", string(addressBytes))
}
/*
&omitempty.coordinate{Lat:1, Lng:0}
{
"latitude": 1
}
*/
這個坐標的longitude消失不見了!
但我們的設想是,如果一個地點沒有經緯度信息,則懸空,這沒有問題,但對於“原點坐標”,我們在確切知道它的經緯度的情況下,(0.0, 0.0)仍然被忽略了。正確的寫法也是將結構體內的定義改為指針
type coordinate struct {
Lat *float64 `json:"latitude,omitempty"`
Lng *float64 `json:"longitude,omitempty"`
}
/*
&omitempty.coordinate{Lat:(*float64)(0xc0000a6288), Lng:(*float64)(0xc0000a6298)}
{
"latitude": 1,
"longitude": 0
}
*/
這樣空值就從 float64 的 0.0 變為了指針類型的 nil ,我們就能看到正確的經緯度輸出。
