> ## 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.

# 五年了， JSON/V2 还是没有转正
- URL: https://huizhou92.com/wu-nian-liao-json-v2-huan-shi-mei-you-zhuan-zheng/
- Published: 2026-02-27T13:50:53.000Z
- Updated: 2026-09-06T11:38:28.000Z
- Description: 五年了， JSON/V2 还是没有转正. 五年了， JSON/V2 还是没有转正 2026 年 2 月 10 日，Go 1.26 重磅发布。 Green Tea 垃圾回收器正式成为默认选项。 go fix 工具经历了全面的现代化改造。cgo 调用开销降低了 30% 。64 位平台迎来了堆地址随机化（Heap。
- Author: huizhou92
- Tags: blog, golang, JSON, standard-library, #lang-en

# 五年了， JSON/V2 还是没有转正

![cover](https://images.hxzhouh.com/blog-images/2026/02/605cdeab11eca0ae50d948387214af5b.png)

2026 年 2 月 10 日，Go 1.26 重磅发布。**Green Tea** 垃圾回收器正式成为默认选项。`go fix` 工具经历了全面的现代化改造。cgo 调用开销降低了 **30%**。64 位平台迎来了堆地址随机化（Heap Address Randomization）安全增强。无论从哪个角度看，这都是一个里程碑式的版本。

然而，许多开发者翘首以盼的功能——`encoding/json/v2`——却明显缺席于稳定 API 之中。它依然躲在 `GOEXPERIMENT=jsonv2` 的实验性标志背后，尚未就绪。

我从Golang 1.25 就开始期待`JSON/v2` 了。**五年**的开发，居然还没完成？但如果你深入研究核心追踪议题 [#76406](https://github.com/golang/go/issues/76406?ref=huizhou92.com) 和相关提案 [#71497](https://github.com/golang/go/issues/71497?ref=huizhou92.com)，画面就变得清晰了：这种延迟不是工程效率的问题。它源于对 **API 完美主义**的执着追求、一场艰苦卓绝的**向后兼容性**攻坚战，以及一个几乎让整个项目脱轨的**内存回退**问题。

我们逐一拆解。

![Pasted image 20260212150624](https://images.hxzhouh.com/blog-images/2026/02/e17a40bcd89d60d0337cfb1ab11085bd.png)

### 宏观视角：为什么 JSON V2 如此重要

当前的 `encoding/json`（我们姑且称之为 v1）已经忠实服务 Go 开发者超过十年。但它的设计缺陷一直在累积，就像一个从不重构的创业公司的技术债务。以下是主要问题清单：

- 它会**默默接受无效的 UTF-8** JSON 字符串
- 它**允许重复键名**而毫无怨言
- 自定义 `Marshaler` 实现**无法访问配置选项**
- 没有办法**拒绝有效 JSON 文档之后的多余数据**
- 所谓的"流式"API 基本上是个幌子——底层实际上会缓冲所有内容

这些绝非小问题。在生产环境中，它们是**安全攻击向量**和**静默数据损坏**的风险源。符合 RFC 8259 标准？差得远呢。

由 **Joe Tsai** 和 **Daniel Martí** 领导的 JSON v2，是自泛型（Generics）落地以来最具雄心的标准库重写。它旨在修复上述所有问题——同时带来巨大的性能提升。

### 全新架构：语法层与语义层的分离

v2 最精妙的设计决策之一，是将 JSON 处理**严格拆分**为两层：

- **`encoding/json/jsontext`** —— 语法层（Syntactic Layer）。负责纯粹的 JSON 标记化（Tokenizing）、解析和编码。无反射，不涉及 Go 类型。你可以把它理解为一个高性能 JSON 扫描器。
- **`encoding/json/v2`** —— 语义层（Semantic Layer）。基于 `jsontext` 构建，实现 Go 类型与 JSON 数据之间的映射。

这种分离的威力在于：如果你只需要验证或转换原始 JSON 而不涉及 Go 结构体，可以直接使用 `jsontext`——零反射开销、真正的流式处理、最小化内存分配。

以下是两个版本的对比：

| **维度**        | **encoding/json (v1)** | **encoding/json/v2 (实验性)**                       |
| ------------- | ---------------------- | ------------------------------------------------ |
| **架构**        | 语法与语义耦合，重度依赖反射         | 清晰分离：jsontext（语法） + v2（语义）                       |
| **流式处理**      | 伪流式；底层往往需要全量缓冲         | 真正的流式编码与解码                                       |
| **默认安全性**     | 接受无效 UTF-8，允许重复键名      | 拒绝无效 UTF-8，拒绝重复键名（符合 RFC 8259）                   |
| **性能**        | 基准                     | Unmarshal 最高提升 **10 倍**；Marshal 提升 **1.6–3.6 倍** |
| **unsafe 使用** | 无                      | 无——在不使用 unsafe 的前提下达到第三方库级别的性能                   |

最后一行值得着重强调。像 **Sonic**（字节跳动）这样的库通过大量使用 `unsafe` 来实现极致速度。而 JSON v2 在**不牺牲内存安全**的前提下达到了同等性能水平。这就是标准库的承诺：你不需要用正确性换取速度。  
![Pasted image 20260212150646](https://images.hxzhouh.com/blog-images/2026/02/1823afa2454141bdb6436bfa9bd64c14.png)

### 为什么没有随 1.26 发布：四大阻碍

既然 v2 这么好，为什么不在 Go 1.26 中发布？追踪议题 [#76406](https://github.com/golang/go/issues/76406?ref=huizhou92.com) 揭示了四个截然不同的战场。

#### 1\. "永久 API" 约束

在 Go 的标准库哲学中，一旦 API 脱离实验阶段进入 `encoding/json` 路径，它就必须受到 **Go 1 兼容性承诺**的保护。永远如此，不可撤回。

Joe Tsai 对此态度明确：性能问题可以后续通过优化来解决，但 **API 设计缺陷一旦固化，就会成为未来几十年的技术债务**。

目前的审计重点集中在 `jsontext` 的公共接口。作为基础层，它的设计需要在高性能与易用性之间取得平衡——尤其是在 Token 处理以及与 `io.Reader`/`io.Writer` 的最佳交互模式上。

此外还有关于 `time.Duration` 的激烈争论。v1 将持续时间序列化为**纳秒整数**——虽然广泛使用，但跨语言互操作性极差。v2 倾向于采用 Go 风格的字符串如 `"1h2m3s"`，但这引发了关于标准化程度的热烈讨论。双方各执己见，API 签名悬而未决。

#### 2\. "完美兼容 json/v1

以下是 Go 团队的雄心壮志：一旦 v2 落地，他们希望**只维护一套代码库**。现有的 `encoding/json` 将变成 v2 引擎的一个薄包装层（Shim）。

问题在于：v2 引擎必须完美复刻 v1 的**所有行为**——包括 bug、未文档化的怪癖，以及成千上万生产应用无意中依赖的边缘情况。

> 例如，v1 在处理非寻址值（Non-addressable Values）的指针接收者 Marshaler 时存在特定的不一致性。许多现有应用在不知情的情况下已经依赖了这种行为。

在一个架构完全不同的引擎中复刻这些细微的、难以文档化的行为偏差，是一项极其艰巨的工程挑战。团队目前正在通过大规模测试逐步"烧除"（Burning Down）发现的每一个微小行为差异。进展稳步推进，但长尾效应依然明显。

#### 3\. 内存回退问题：Issue #75026

这是最令人警醒的阻碍。在 Go 1.25 和 1.26 的实验周期中，Issue [#75026](https://github.com/golang/go/issues/75026?ref=huizhou92.com) 报告了在特定 Map 序列化场景下**灾难性的内存分配回退**。

| **场景（Map 编码）**                | **内存分配 (B/op) — v1 原生** | **内存分配 (B/op) — v2 实验** | **增幅**      |
| ----------------------------- | ----------------------- | ----------------------- | ----------- |
| KeyCount: 10, ValLength: 1000 | 832                     | 33,139                  | **+3,883%** |

没有看错。一个常见的 Map 编码模式，内存分配量增加了 **39 倍**。

性能分析显示，**92.98%** 的内存分配集中在 `bytes.growSlice` 中，这表明新架构在处理复杂对象树或特定 Map 结构时存在严重的缓冲区管理缺陷。虽然总的分配次数（`Allocs/op`）实际上有所下降，但单次分配的体积暴增，给 GC 带来了巨大压力。

如果这种回退作为 v1 用户的默认行为发布，后果将是灾难性的。修复这些极端场景下的分配逻辑是目前**最高优先级**的工作。

#### 4\. 生态成熟度与联合类型之争

JSON v2 项目已经开发了**五年**。虽然已在许多生产环境中得到验证，但在成为默认标准之前，仍需经历更广泛的生态系统压力测试。Project 50 的追踪显示，44 个子任务中仍有约 **18 个处于开放状态**或需要进一步审计。

此外，关于 v2 是否应该支持 JSON 反序列化中的**联合类型**（Union Types / Sum Types），也引发了设计上的分歧。部分开发者认为这是现代 JSON 处理的标配功能。Go 团队的立场？等待语言层面的**总和类型提案**（如 [#57644](https://github.com/golang/go/issues/57644?ref=huizhou92.com)）成熟后再做集成，而不是在 JSON 包中塞入一个临时的、不兼容的实现。

这就是典型的 Go 式实用主义：不在库层面解决语言层面的问题。

### 值得等待的核心特性

尽管发布延迟，v2 的实验性版本已经展示了多项将从根本上改变 Go JSON 处理模式的特性。

#### `omitzero` —— 终于有了合理的省略逻辑

v1 的 `omitempty` 让开发者困惑了多年。`time.Time{}` 算空吗？`false` 算空吗？答案是不一致的，而且常常出人意料。

v2 引入了 `omitzero`，它严格按照 Go 的**零值**语义进行判断，并支持自定义 `IsZero() bool` 接口：

```go
type Event struct {
    Name      string    `json:"name"`
    StartTime time.Time `json:"start_time,omitzero"`
    EndTime   time.Time `json:"end_time,omitzero"`
}

```

这对于 **PATCH 风格的 API** 尤其有用——你只需序列化那些被明确修改过的字段，而不会因为字段恰好持有类型的默认零值而意外忽略它们。

#### `inline` 和 `unknown` —— 灵活的数据建模

两个新的结构体标签解决了长期存在的痛点：

- **`inline`**：将嵌套结构体或 Map 的内容"平铺"到父 JSON 对象中——无需匿名嵌入。对于包含动态键值对的 API 来说，这是一个巨大的改进。
- **`unknown`**：指定一个字段（通常是 `map[string]jsontext.Value`）来捕获结构体中未定义的所有 JSON 成员。彻底告别"二次反序列化"的开销。

```go
type Config struct {
    Version  int                          `json:"version"`
    Name     string                       `json:"name"`
    Extra    map[string]jsontext.Value    `json:",unknown"`
}

```

#### `format` —— 内置编码定制

`format` 选项支持按字段定制编码方式：

- 为 `[]byte` 字段定制 Base64 或 Hex 编码
- 为 `time.Time` 使用自定义布局字符串
- 不再需要为常见格式化需求编写自定义 Marshaler/Unmarshaler 实现

### 性能：Benchmark 数据说话

基于 `jsonbench` 评估套件，以下是 v2 与竞品的对比：

| **库**             | **Marshal 速度**   | **Unmarshal 速度**  | **内存安全性**      |
| ----------------- | ---------------- | ----------------- | -------------- |
| **JSON v1**       | 基准               | 基准                | 高（无 unsafe）    |
| **JSON v2**       | **提升 1.6–3.6 倍** | **提升 2.7–10.2 倍** | 高（无 unsafe）    |
| **Sonic（字节跳动）**   | 与 v2 相当          | 极快（部分使用 unsafe）   | 中（大量使用 unsafe） |
| **JSON-Iterator** | 慢于 v2            | 慢于 v2             | 中              |

关键洞察：v2 通过**迭代式线性解析**实现性能飞跃，而非 v1 的逐字节虚函数扫描模式。而且它没有使用一个 `unsafe.Pointer`。这不仅仅是快——这是**负责任地快**。

### 前路：v2 何时落地？

![Pasted image 20260212150729](https://images.hxzhouh.com/blog-images/2026/02/44e7b95ea138d8dd9d69c62bc2139da6.png)

基于当前的任务处理速度和 [#76406](https://github.com/golang/go/issues/76406?ref=huizhou92.com) 上的开发者活跃度，以下是务实的时间线：  
**2026 年上半年：**

1. **修复 Issue #75026** —— 解决 Map 编码的内存回退问题。这是脱离实验阶段的唯一最关键前提。
2. **API 定稿** —— 完成 `jsontext` 中所有公共函数的审计，特别是 `Encoder`/`Decoder` 在流式场景中的状态机健壮性。
3. **v1 兼容层完善** —— 确保所有已知的 v1 行为（包括那些"有 bug 的"行为）在 v2 引擎中都有对应的配置选项支持。

**Go 1.27（预计 2026 年 8 月）：**

这被广泛认为是 JSON v2 移除实验性标签、进入稳定标准库的**最早也是最可能的窗口**。1.27 开发周期中代码树（Tree）的重新开放将是需要密切关注的关键信号。

Go 团队还计划在 v2 正式发布时同步推出**现代化迁移工具**。重新设计的 `go fix` 将支持从 v1 到 v2 默认配置的一键迁移——不只是简单的字符串替换，而是感知类型信息的智能转换。例如，它能检测到手动实现的 Base64 转换逻辑，并建议替换为 v2 的 `format:base64` 结构体标签。

### 总结

1. **JSON v2 的延迟不是失败——而是纪律。** Go 团队拒绝发布一个存在设计缺陷的 API，因为在 Go 1 兼容性保证下，这些缺陷将永久存在。
2. **架构设计是扎实的。** 通过 `jsontext` 和 `v2` 实现的语法/语义分离是一种精妙且面向未来的设计。
3. **性能已经得到验证。** Unmarshal 最高提升 10 倍，Marshal 提升 3.6 倍——全程不使用 `unsafe`。
4. **内存回退（#75026）是关键阻碍。** Map 编码中 39 倍的分配增长必须在 v2 成为默认之前解决。
5. **Go 1.27（2026 年 8 月）是目标。** 社区应据此做好规划。

**对开发者的实用建议：**

- **内部工具**：放心在非核心系统中尝试 `GOEXPERIMENT=jsonv2`。仅 `omitzero` 和 `unknown` 特性就能显著简化你的代码。提交 Bug 报告——团队需要它们。
- **性能敏感型应用**：如果 JSON 反序列化是你的 CPU 瓶颈，v2 的实验版本可能已经优于 v1——而且比第三方 `unsafe` 库更安全。
- **公共组件库**：目前继续使用 `encoding/json`（v1）。v2 的 API 仍有可能发生破坏性变更，你的用户不会欣赏这种不稳定性。

JSON v2 将是 Go 语言自泛型以来最重要的库级演进。它不仅让速度更快——更补全了 Go 在现代数据交换标准合规性上长达十年的缺口。随着阻碍一个接一个地被清除，一个更安全、更快速、更灵活的 JSON 处理时代即将随 Go 1.27 到来。

等待即将结束。而且，值得。

**参考资料：**

- Go Issue [#76406](https://github.com/golang/go/issues/76406?ref=huizhou92.com) — JSON v2 追踪议题
- Go Proposal [#71497](https://github.com/golang/go/issues/71497?ref=huizhou92.com) — encoding/json/v2 提案
- Go Issue [#75026](https://github.com/golang/go/issues/75026?ref=huizhou92.com) — jsonv2 实验中的内存分配回退
- Go Issue [#57644](https://github.com/golang/go/issues/57644?ref=huizhou92.com) — 总和类型语言提案
- [Go 1.26 Release Notes](https://go.dev/doc/go1.26?ref=huizhou92.com)