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