Go 1-24 的 `omitzero`:JSON 處理的福音
使用 omitempty
忽略 JSON 中的可選字段 (Go 1.24 之前)
當你有一個要轉換爲 JSON 的結構體時,你可能有一些字段是可選的。
例如,我們採用以下結構體:
type Response struct {
ID string `json:"id"`
// 其他字段省略
UpdatedAt time.Time `json:"updated_at"`
}
假設 UpdatedAt 應該只在數據庫中的底層記錄在創建後更新時才設置。
考慮到這一點,我們可能只想在 JSON 響應中返回該字段的值(如果它有值)。
如果我們使用上面的結構體,並 marshal 數據(UpdatedAt 爲其零值):
func main() {
resp := Response{
ID: "the-id",
// 沒有顯式初始化 UpdatedAt
}
data, err := json.MarshalIndent(resp, "", " ")
must(err)
fmt.Printf("%s\n", data)
}
func main() {
resp := Response{
ID: "the-id",
// 沒有顯式初始化 UpdatedAt
}
data, err := json.MarshalIndent(resp, "", " ")
must(err)
fmt.Printf("%s\n", data)
}
這會導致一個不需要的 updated_at JSON 字段:
{
"id": "the-id",
"updated_at": "0001-01-01T00:00:00Z"
}
在 Go 1.23(及之前)中,使 updated_at 可選的最佳方法是使用可選指針和 omitempty JSON struct tag:
type Response struct {
ID string `json:"id"`
UpdatedAt *time.Time `json:"updated_at,omitempty"`
}
這允許你不指定 UpdatedAt,導致其零值 (nil),這將通過 omitempty struct tag 被視爲不存在,因此不會被 marshal:
{
"id": "the-id"
}
這種方式能實現我們的目標,但使用起來也可能有點尷尬,因爲你現在必須使用指針。
使用 omitzero 忽略 JSON 中的可選字段 (Go 1.24 之後)
但是,從 Go 1.24 開始,我們現在有了 omitzero JSON struct tag,它允許我們使用類型的實際零值來指示它不被 marshal。
這允許我們使用以下結構體定義:
type Response struct {
ID string `json:"id"`
UpdatedAt time.Time `json:"updated_at,omitzero"`
}
請注意,我們現在可以使用非指針類型,這改善了我們與類型交互的方式,並且它按預期 marshal:
{
"id": "the-id"
}
如前所述,一個關鍵的事情是減少處理大量額外指針。 雖然在這個人爲的例子中,只有一個指針,但是當嵌套具有可選字段的結構體時,這會變得非常尷尬:
type Response struct {
Human *Human `json:"human"`
}
type Human struct {
DNA []byte `json:"dna"`
Name string `json:"name"`
Age *int `json:"age"`
}
這使得一次性初始化結構變得更加困難,因爲你無法獲取常量值的指針來設置 Age 的值,例如:&30。
return Response{
Human: &Human{
Age: &30, // 🛑 無效操作:無法獲取 30 (未確定類型的整數常量) 的地址
},
}
當使用這樣的代碼時,這可能有點麻煩,並且變得相當重複,所以如果你可以避免指針繁重的類型,那就太棒了!
正如 omitzero 的提案中所述,這也具有更清晰的語義,因爲 struct{} 可以被稱爲 "empty",但不會根據 omitempty 被視爲 "empty"。
本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源:https://mp.weixin.qq.com/s/dl6HWWhk2EDlcg3q6PbrLA