2026 年 2 月 10 日,Go 1.26 重磅发布。Green Tea 垃圾回收器正式成为默认选项。go fix 工具经历了全面的现代化改造。cgo 调用开销降低了 30%。64 位平台迎来了堆地址随机化(Heap Address Randomization)安全增强。无论从哪个角度看,这都是一个里程碑式的版本。
然而,许多开发者翘首以盼的功能——encoding/json/v2——却明显缺席于稳定 API 之中。它依然躲在 GOEXPERIMENT=jsonv2 的实验性标志背后,尚未就绪。
我从Go 1.25 就开始期待JSON/v2 了。五年的开发,居然还没完成?但如果你深入研究核心追踪议题 #76406[1] 和相关提案 #71497[2],画面就变得清晰了:这种延迟不是工程效率的问题。它源于对 API 完美主义的执着追求、一场艰苦卓绝的向后兼容性攻坚战,以及一个几乎让整个项目脱轨的内存回退问题。
我们逐一拆解。

当前的 encoding/json(我们姑且称之为 v1)已经忠实服务 Go 开发者超过十年。但它的设计缺陷一直在累积,就像一个从不重构的创业公司的技术债务。以下是主要问题清单:
Marshaler 实现无法访问配置选项这些绝非小问题。在生产环境中,它们是安全攻击向量和静默数据损坏的风险源。符合 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 在不牺牲内存安全的前提下达到了同等性能水平。这就是标准库的承诺:你不需要用正确性换取速度。

既然 v2 这么好,为什么不在 Go 1.26 中发布?追踪议题 #76406[3] 揭示了四个截然不同的战场。
在Go的标准库哲学中,一旦API 脱离实验阶段进入 encoding/json 路径,它就必须受到 Go 1 兼容性承诺的保护。永远如此,不可撤回。
Joe Tsai 对此态度明确:性能问题可以后续通过优化来解决,但 API 设计缺陷一旦固化,就会成为未来几十年的技术债务。
目前的审计重点集中在 jsontext 的公共接口。作为基础层,它的设计需要在高性能与易用性之间取得平衡——尤其是在 Token 处理以及与 io.Reader/io.Writer 的最佳交互模式上。
此外还有关于 time.Duration 的激烈争论。v1 将持续时间序列化为纳秒整数——虽然广泛使用,但跨语言互操作性极差。v2 倾向于采用 Go 风格的字符串如 "1h2m3s",但这引发了关于标准化程度的热烈讨论。双方各执己见,API 签名悬而未决。
以下是 Go 团队的雄心壮志:一旦 v2 落地,他们希望只维护一套代码库。现有的 encoding/json 将变成 v2 引擎的一个薄包装层(Shim)。
问题在于:v2 引擎必须完美复刻 v1 的所有行为——包括 bug、未文档化的怪癖,以及成千上万生产应用无意中依赖的边缘情况。
例如,v1 在处理非寻址值(Non-addressable Values)的指针接收者 Marshaler 时存在特定的不一致性。许多现有应用在不知情的情况下已经依赖了这种行为。
在一个架构完全不同的引擎中复刻这些细微的、难以文档化的行为偏差,是一项极其艰巨的工程挑战。团队目前正在通过大规模测试逐步"烧除"(Burning Down)发现的每一个微小行为差异。进展稳步推进,但长尾效应依然明显。
这是最令人警醒的阻碍。在 Go1.25 和1.26 的实验周期中,Issue #75026[4] 报告了在特定 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 用户的默认行为发布,后果将是灾难性的。修复这些极端场景下的分配逻辑是目前最高优先级的工作。
JSON v2 项目已经开发了五年。虽然已在许多生产环境中得到验证,但在成为默认标准之前,仍需经历更广泛的生态系统压力测试。Project 50 的追踪显示,44 个子任务中仍有约 18 个处于开放状态或需要进一步审计。
此外,关于 v2 是否应该支持 JSON 反序列化中的联合类型(Union Types / Sum Types),也引发了设计上的分歧。部分开发者认为这是现代 JSON 处理的标配功能。Go 团队的立场?等待语言层面的总和类型提案(如 #57644[5])成熟后再做集成,而不是在 JSON 包中塞入一个临时的、不兼容的实现。
这就是典型的 Go 式实用主义:不在库层面解决语言层面的问题。
尽管发布延迟,v2 的实验性版本已经展示了多项将从根本上改变 Go JSON 处理模式的特性。
omitzero: 终于有了合理的省略逻辑v1 的 omitempty 让开发者困惑了多年。time.Time{} 算空吗?false 算空吗?答案是不一致的,而且常常出人意料。
v2 引入了 omitzero,它严格按照 Go 的零值语义进行判断,并支持自定义 IsZero() bool 接口:
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 成员。彻底告别"二次反序列化"的开销。
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 使用自定义布局字符串基于 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。这不仅仅是快——这是负责任地快。

基于当前的任务处理速度和 #76406[6] 上的开发者活跃度,以下是可能的时间线:2026 年上半年:
jsontext 中所有公共函数的审计,特别是 Encoder/Decoder 在流式场景中的状态机健壮性。Go 1.27(2026 年 8 月):
这被广泛认为是 JSON v2 移除实验性标签、进入稳定标准库的最早也是最可能的窗口。1.27 开发周期中代码树(Tree)的重新开放将是需要密切关注的关键信号。
Go 团队还计划在 v2 正式发布时同步推出现代化迁移工具。重新设计的 go fix 将支持从 v1 到 v2 默认配置的一键迁移——不只是简单的字符串替换,而是感知类型信息的智能转换。例如,它能检测到手动实现的 Base64 转换逻辑,并建议替换为 v2 的 format:base64 结构体标签。
jsontext 和 v2 实现的语法/语义分离是一种精妙且面向未来的设计。unsafe。对开发者的实用建议:
GOEXPERIMENT=jsonv2。仅 omitzero 和 unknown 特性就能显著简化你的代码。提交 Bug 报告——团队需要它们。unsafe 库更安全。encoding/json(v1)。v2 的 API 仍有可能发生破坏性变更,你的用户不会欣赏这种不稳定性。JSON v2 将是 Go 语言自泛型以来最重要的库级演进。它不仅让速度更快——更补全了 Go 在现代数据交换标准合规性上长达十年的缺口。随着阻碍一个接一个地被清除,一个更安全、更快速、更灵活的 JSON 处理时代即将随 Go 1.27 到来。
参考资料:
[1]#76406: https://github.com/golang/go/issues/76406
[2]#71497: https://github.com/golang/go/issues/71497
[3]#76406: https://github.com/golang/go/issues/76406
[4]#75026: https://github.com/golang/go/issues/75026
[5]#57644: https://github.com/golang/go/issues/57644
[6]#76406: https://github.com/golang/go/issues/76406
[7]#76406: https://github.com/golang/go/issues/76406
[8]#71497: https://github.com/golang/go/issues/71497
[9]#75026: https://github.com/golang/go/issues/75026
[10]#57644: https://github.com/golang/go/issues/57644
[11]Go 1.26 Release Notes: https://go.dev/doc/go1.26