> ## Content Index
> Fetch the complete content index at: https://huizhou92.com/llms.txt
> Use this file to discover other available public pages before exploring further.

# Go 提案: JSON v2 来了
- URL: https://huizhou92.com/go-ti-an-json-v2-lai-liao/
- Published: 2024-09-15T02:05:57.000Z
- Updated: 2026-09-06T11:39:37.000Z
- Description: Go 提案: JSON v2 来了. 背景 JSON 是一种轻量级的数据交换格式，Go 语言的 encoding/json 包自诞生以来已有十年之久，它凭借其灵活的特性赢得了开发者的青睐。开发者可以通过结构体标签自定义字段的 JSON 表现形式，也可以让 Go 类型自定义其在 JSON 中的表现形式。然而，随。
- Author: huizhou92
- Tags: tech, go, JSON, go1_24, #lang-zh-cn

## 背景

`JSON`是一种轻量级的数据交换格式，Go 语言的 `encoding/json` 包自诞生以来已有十年之久，它凭借其灵活的特性赢得了开发者的青睐。开发者可以通过结构体标签自定义字段的 JSON 表现形式，也可以让 Go 类型自定义其在 JSON 中的表现形式。然而，随着 Go 语言和 JSON 标准的发展，一些功能上的缺失和性能上的限制逐渐暴露出来。

1. **功能缺失**：比如，不能指定 `time.Time` 类型的自定义格式化，不能在序列化时省略特定的 Go 值等。
2. **API 缺陷**：比如，没有简单的方法来正确地从 `io.Reader` 中反序列化 JSON。
3. **性能限制**：标准`json`包的性能表现并不尽如人意，特别是在处理大量数据时。
4. **行为缺陷**：比如，对 JSON 语法的错误处理不够严格，大小写不敏感的反序列化等。

就像`math/v2`一样的，Go 官方提出[encoding/json/v2](https://github.com/golang/go/discussions/63397?ref=huizhou92.com) 来解决上面的那些问题，本文主要目标是分析enable/json中 关于空值的一些问题。以及它在 [encoding/json/v2](https://github.com/golang/go/discussions/63397?ref=huizhou92.com)中是如何被解决的。本文不涉及 [encoding/json/v2](https://github.com/golang/go/discussions/63397?ref=huizhou92.com) 中其他的修改

> This article was first published in the Medium MPP plan. If you are a Medium user, please follow me on [Medium](https://medium.huizhou92.com/?ref=huizhou92.com). Thank you very much.

## Omitempty

在[encoding/json](https://pkg.go.dev/encoding/json?ref=huizhou92.com)包中，有这样关于`omitempty`的描述:

> The "omitempty" option specifies that the field should be omitted from the encoding if the field has an empty value, defined as false, 0, a nil pointer, a nil interface value, and any empty array, slice, map, or string.

但是，这种预定义的”空”值判断逻辑并不能满足所有实际场景的需求。我们来看一个例子：

```go
  
type Post struct {  
    Id         int64           `json:"id,omitempty"`  
    CreateTime time.Time       `json:"create_time,omitempty"`  
    TagList    []Tag           `json:"tag_list,omitempty"`  
    Name       string          `json:"name,omitempty"`  
    Score      float64         `json:"score,omitempty"`  
    Category   Category        `json:"category,omitempty"`  
    LikePost   map[string]Post `json:"like,omitempty"`  
}  
type Tag struct {  
    ID   string `json:"id"`  
    Name string `json:"name"`  
}  
type Category struct {  
    ID   float64 `json:"id"`  
    Name string  `json:"name"`  
}  
  
func main() {  
    b, _ := json.Marshal(new(Post))  
    fmt.Println(string(b))  
}

```

输出结果为：

```json
{"create_time":"0001-01-01T00:00:00Z","category":{"id":0,"name":""}}

```

虽然`Post` 各个字段都添加了`omitempty` 。但是结果并不像我们预期一样。

1. `omitempty` 不能处理空 `struct` ，比如 `Post.Category`
2. `omitempty` 处理`time.Time` 的方式不是 我们理解的`UTC =0`，也就是`1970-01-01 00:00:00` ,而是 `0001-01-01T00:00:00Z` 。

## Omitzero标签

在 [encoding/json/v2](https://github.com/golang/go/discussions/63397?ref=huizhou92.com) 中，会添加一个新的标签 `omitzero`，添加了两个功能，来解决上面的两个问题。（目前这个功能还在开发中，不过可以通过[go-json-experiment/json](https://github.com/go-json-experiment/json?ref=huizhou92.com)提前体验新功能）

1. 更好的`time.Time` 处理。
2. 支持自定义`IsZero`函数。  
比如下面下面的代码：

```go
package main  
  
import (  
    "encoding/json"  
    "fmt"    v2_json "github.com/go-json-experiment/json"  
    "math"    "time")  
  
type Post struct {  
    Id         int64           `json:"id,omitempty,omitzero"`  
    CreateTime time.Time       `json:"create_time,omitempty,omitzero"`  
    TagList    []Tag           `json:"tag_list,omitempty"`  
    Name       string          `json:"name,omitempty"`  
    Score      ScoreType       `json:"score,omitempty,omitzero"`  
    Category   Category        `json:"category,omitempty,omitzero"`  
    LikePost   map[string]Post `json:"like,omitempty"`  
}  
type ScoreType float64  
  
func (s ScoreType) IsZero() bool {  
    return s < math.MinInt64  
}  
  
type Tag struct {  
    ID   string `json:"id"`  
    Name string `json:"name"`  
}  
type Category struct {  
    ID   float64 `json:"id"`  
    Name string  `json:"name"`  
}  
  
func main() {  
    v1String, _ := json.Marshal(new(Post))  
    fmt.Println(string(v1String))  
    v2String, _ := v2_json.Marshal(new(Post))  
    fmt.Println(string(v2String))  
}

```

跟`encoding/json` 相比 `encoding/json/v2` 解决了上面提到的问题。

```json
{"create_time":"0001-01-01T00:00:00Z","category":{"id":0,"name":""}}
{"score":0}

```

## 结论

通过引入`omitzero`标签，Go语言在解决JSON编码中”空”值处理的痛点上迈出了重要一步。这个方案不仅满足了开发者对更灵活的”空”值定义的需求，还保持了与现有系统的兼容性。目前该`omitzero`的落地时间尚未确定，最早也要等到`Go 1.24`版本。此外，encoding/xml等也会效仿json包，增加`omitzero`标签。  
[encoding/json/v2](https://github.com/golang/go/discussions/63397?ref=huizhou92.com) 还包括 性能等其他方面的更新， 有兴趣的Gopher 可以 提前了解这些改动，本博客也会一直关注这个proposal  
Go语言开始走向成熟。