Go語言的omitempty


使用

熟悉 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 ,我們就能看到正確的經緯度輸出。


免責聲明!

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



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