はじめに
2025年2月11日にGoの1.24がリリースされました。
そこで、今回は更新点に軽く触れつつ、新しく追加された
encoding/json
の
omitzero
タグについて紹介します。
1.24の更新点
Go 1.24では多くの機能追加や変更点があります。その中で目に止まったものをいくつか列挙します。
-
go get
コマンドに-tool
フラグを追加 - Runtimeの改善によりCPUのoverheadが平均2〜3%ほど減少した
-
os.Root
型の追加 - 弱い参照を提供する
weak
パッケージの追加 - 暗号化関連の機能強化多数
-
bytes
パッケージとstrings
パッケージに iterator を扱う関数が追加 -
encoding/json
パッケージにomitzero
タグを追加
詳細については、 Release Notes をご覧ください。
jsonのomitzeroタグ
omitzero
タグは構造体をMarshalする際にフィールドがゼロ値の場合は省略してくれるオプションです。
似たようなオプションに
omitempty
タグがありますが、こちらは
0
、
""
、
nil
の場合には省略されます。しかし、構造体の場合は省略されず以下のようになってしまいます。
1 | {"At":"0001-01-01T00:00:00Z"} |
これが、
omitzero
タグを使うことで解決できます。
Go 1.23までの場合
まずは、これまで通り
omitempty
タグを使った場合を見ていきます。
以下のようになります。
コード
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | package main import ( "encoding/json" "fmt" "time" ) type Book struct { Name string `json:",omitempty"` Price int `json:",omitempty"` At time.Time `json:",omitempty"` } func main() { b := &Book{} m, _ := json.Marshal(b) fmt.Println(string(m)) } |
結果
1 | {"At":"0001-01-01T00:00:00Z"} |
このように
time.Time
だけは初期値で含まれてしまっています。これを回避するために、あえてポインタ型にする対応が取られることがありました。
コード
1 2 3 4 5 | type Book struct { Name string `json:",omitempty"` Price int `json:",omitempty"` At *time.Time `json:",omitempty"` } |
結果
1 | {} |
Go 1.24での場合
omitempty
の代わりに
omitzero
を使った場合以下のようになります。
コード
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | package main import ( "encoding/json" "fmt" "time" ) type Book struct { Name string `json:",omitzero"` Price int `json:",omitzero"` At time.Time `json:",omitzero"` } func main() { b := &Book{} m, _ := json.Marshal(b) fmt.Println(string(m)) } |
結果
1 | {} |
このように、ポインタ型にしなくても省略することができました。
要素が空の場合とnilの場合
omitempty
の場合、要素が空であっても、
nil
でも区別なく省略されてしまいますが、
omitzero
の場合、これらは別々のものとして扱われます。
コード
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | package main import ( "encoding/json" "fmt" ) type Foo struct { List1 []string `json:",omitempty"` List2 []string `json:",omitempty"` List3 []string `json:",omitzero"` List4 []string `json:",omitzero"` Map1 map[string]string `json:",omitempty"` Map2 map[string]string `json:",omitempty"` Map3 map[string]string `json:",omitzero"` Map4 map[string]string `json:",omitzero"` } func main() { b := &Foo{ List1: []string{}, List3: []string{}, Map1: map[string]string{}, Map3: map[string]string{}, } m, _ := json.Marshal(b) fmt.Println(string(m)) } |
結果
1 | {"List3":[],"Map3":{}} |
このように、
omitzero
の場合、要素が空の場合は省略されません。
omitzeroをコントロールする
omitzero
で省略するかどうかは
IsZero
メソッドで判定されます。そのため、自分で定義した構造体にこのメソッドを定義することで、どのような状態の場合に省略するのかコントロールすることができます。
コード
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 | package main import ( "encoding/json" "fmt" ) type Market struct { Food *Food `json:",omitzero"` } type Food struct { Name string Price int Stock int } func (f *Food) IsZero() bool { return f.Stock == 0 } func main() { m1 := &Market{ Food: &Food{ Name: "Apple", Price: 100, Stock: 10, }, } m, _ := json.Marshal(m1) fmt.Println(string(m)) m2 := &Market{ Food: &Food{ Name: "Pear", Price: 80, Stock: 0, }, } m, _ = json.Marshal(m2) fmt.Println(string(m)) } |
結果
1 2 | {"Food":{"Name":"Apple","Price":100,"Stock":10}} {} |
このように
IsZero
メソッドの戻り値が
true
の場合は省略されるようになります。
さいごに
encoding/json
に追加された
omitzero
タグについて紹介しました。
大きな変更点というわけではありませんが、かゆいところに手が届く良い機能だと思います。