Go 1.24 omitzero
解決 omitempty 的限制
Go 1.24 新增了一個新的 struct tag omitzero,解決了 omitempty 無法忽略 struct 以及 nil vs empty slice/map 不可區分的問題。
在介紹 omitzero 之前,讓我們先回顧 omitempty 的行為。
omitempty
"omitempty"指定當字段為「空值」時,序列化時應忽略該字段。「空值」的定義:
false、0、nil指標、nil介面,以及長度為 0 的 array、slice、map、string。
type Foo struct {
Int int `json:"int,omitempty"`
Str string `json:"str,omitempty"`
Arr []int `json:"arr,omitempty"`
Map map[string]int `json:"map,omitempty"`
Bar Bar `json:"bar,omitempty"`
BarPtr *Bar `json:"bar_ptr,omitempty"`
}
type Bar struct {
Val int `json:"val"`
}
func main() {
foo := Foo{
Int: 0,
Str: "",
Arr: []int{},
Map: map[string]int{},
Bar: Bar{}, // 這裡是關鍵!
BarPtr: nil,
}
bs, _ := json.MarshalIndent(foo, "", " ")
fmt.Println(string(bs))
}
結果
{
"bar": {
"val": 0
}
}
關鍵點:
omitempty會忽略0、""、nil、空 slice/map。但 struct (
Bar) 無法忽略,即使Val=0也是如此!空 slice/map (
Arr、Map) 直接被忽略,無法區分nil與empty。
omitzero
"omitzero"指定當字段為「零值」時,應忽略該字段。
「零值」的定義:
若該類型有
IsZero() bool方法,則使用該方法的結果。否則,該類型的「零值」就是該類型的預設值 (
0,false,"",nil等)。若同時標記
omitempty和omitzero,則符合任何一種條件都會被忽略。
type Foo struct {
Int int `json:"int,omitzero"`
Str string `json:"str,omitzero"`
Arr []int `json:"arr,omitzero"`
Map map[string]int `json:"map,omitzero"`
Bar Bar `json:"bar,omitzero"`
BarPtr *Bar `json:"bar_ptr,omitzero"`
}
type Bar struct {
Val int `json:"val"`
}
func main() {
foo := Foo{
Int: 0,
Str: "",
Arr: []int{},
Map: map[string]int{},
Bar: Bar{}, // 這裡是關鍵!
BarPtr: nil,
}
bs, _ := json.MarshalIndent(foo, "", " ")
fmt.Println(string(bs))
}
結果
{
"arr": [],
"map": {}
}
關鍵點:
struct (
Bar) 被忽略了! 只要它的所有欄位都是「零值」,就會被省略。slice/map 只會忽略
nil,不會忽略len=0的情況。
IsZero():自訂「零值」邏輯
允許 struct 定義 IsZero() 方法,讓 omitzero 可依據業務需求決定何時省略 struct。
type Bar struct {
Enable bool `json:"enable"`
Val int `json:"val"`
}
func (b Bar) IsZero() bool {
return !b.Enable // 只有 Enable=false 才視為零值
}
type Foo struct {
Bar Bar `json:"bar,omitzero"`
}
foo1 := Foo{Bar: Bar{Enable: false, Val: 1}}
// 結果:{}
foo2 := Foo{Bar: Bar{Enable: true, Val: 1}}
// 結果:{"bar":{"enable":true,"val":1}}
這讓我們可以更細緻地控制何時省略 struct,例如:
Enable=false時,整個 struct 都被忽略,即使Val != 0。Enable=true時,則保留整個 struct。
omitempty vs omitzero:比較表
| 類型 | omitempty | omitzero |
| int, float, string, bool, ptr | 0, false, "", nil | 0, false, "", nil |
| slice/map | len=0 (包含 nil) | nil (不包含 len=0) |
| struct | 無法忽略 | 有 IsZero() 則判斷 IsZero() == true 否則所有欄位為零值時忽略 |
結論
omitempty適合「只忽略確定為空的標準類型」,但對 struct 無效,且無法區分nil和empty的 slice/map。omitzero更強大,讓 struct 也能被忽略,並且允許IsZero()來自訂「零值」邏輯。若要更細緻地控制 Go 的 JSON 序列化,
omitzero是更靈活的選擇。
