本文永久链接 – https://tonybai.com/2026/11/07/why-go-json-v2-took-six-years
大家好,我是Tony Bai。
【导读】
JSON 是互联网世界最普遍的数据交换格式之一,也是 Go 标准库中使用最广泛的组件之一。
但你可能不知道,Go 团队为了重写 encoding/json(即下面多次提到的 JSON v2),前后花费了将近六年时间。
从 2020 年的最初构想到 2023 年的社区讨论,从 2025 年 Go 1.25 的实验性发布,到 2026 年 Go 1.27 正式稳定,encoding/json/v2 终于走完了这段漫长的旅程。
这不是一次简单的性能优化,而是一次涉及 API 设计、历史兼容性、底层架构、社区协作和生态迁移的系统性重构。
Go 团队究竟遇到了什么问题?为什么不能直接修复旧版?六年时间又沉淀出了怎样的工程经验?
【文章要点】
- 六年马拉松的终点:Go 1.27 正式将 encoding/json/v2 和 encoding/json/jsontext 标记为稳定可用,宣告自 2020 年由 Daniel Martí、Joe Tsai 等社区骨干发起的长周期重构顺利落地;
- 旧版为何不能直接修:受制于 Go 1 严格的向后兼容承诺,旧版在大小写模糊匹配、nil slice 编码为 null、接受非法 UTF-8 等历史行为已成为既有契约,直接修正会破坏下游海量业务,必须另辟 v2 命名空间;
- 语法与语义彻底解耦的双层架构:将原本交织的解析逻辑拆解为独立的 jsontext(专注于 JSON 语法、Token 流式处理,脱离 Go 反射依赖)与高层的 v2(专注于 Go 结构体映射、自定义 Marshaler 与可配置 Options),实现模块化独立演进;
- 共用底层引擎实现平滑共存:旧版 encoding/json 绝不废弃,而是直接由 v2 的底层实现驱动,存量项目无需修改一行代码即可获得反序列化最高达 10 倍的性能收益与持续维护保障;
- 克制演进的工程课:这不仅是一次序列化库的代码升级,更是大型基础软件生态在面临严重历史包袱时,如何通过“识别问题—设计分层—原型验证—社区迭代—双轨过渡”完成系统性重构的标准范本。

2026 年 11 月 4 日,Go 官方博客发布了一篇新文章:《A new Go API for JSON》。
这篇文章并没有带来什么全新的 API,也不是一次普通的 Go 1.27 特性介绍。它更像是一份迟到了六年的项目总结:Go 团队终于可以正式宣布,encoding/json/v2 和 encoding/json/jsontext 已经在 Go 1.27 中稳定发布。
从 2020 年最初的设计构想到今天,这项工作已经走过了将近六年。
有意思的是,JSON 本身并不复杂。它只有对象、数组、字符串、数字、布尔值和 null 几种基本结构。
那么,为什么一个 JSON 库的重写,竟然需要如此漫长的时间?
答案藏在 Go 标准库的历史包袱、API 设计以及 Go 社区的工程文化之中。
一个不能轻易修改的老牌标准库
Go 对 JSON 的支持,远比很多人想象得更早。
在 Go 1.0 正式发布之前,标准库就已经提供了 encoding/json。十多年过去,它早已成为 Go 生态最重要的基础组件之一。
Go 官方在 2025 年的实验版介绍中提到,encoding/json 已经是 Go 项目中被导入次数排名第五的包。
然而,使用范围越广,重构的代价也就越大。
经过多年发展,旧版 encoding/json 暴露出了一些设计缺陷:
- 标准合规性不足:接受非法 UTF-8 数据,也允许 JSON 对象出现重复字段名。
- 默认行为不够合理:nil slice 和 nil map 会被编码成
null,而不是空数组和空对象。 - 字段匹配过于宽松:反序列化时采用大小写不敏感匹配。
- API 扩展受限:自定义
MarshalJSON和UnmarshalJSON方法难以获得调用方配置的选项。 - 流式处理能力不足:传统接口需要先构造完整的 JSON 字节切片,难以真正做到高效流式处理。
这些问题并不是最近才被发现的。
事实上,Go 社区早已围绕它们讨论多年。
那么,为什么不直接修复?
这里涉及 Go 最重要的工程原则之一:Go 1 兼容性承诺。
一个被大量项目依赖的标准库,其既有行为已经成为事实上的 API 契约。
例如,旧版允许大小写不敏感匹配。如果突然改成严格匹配,可能导致某些一直正常运行的业务系统无法解析原有数据。
同样,nil slice 从 null 改成 [],虽然在很多场景下更加合理,却依然会改变程序输出。
这些改变可能是正确的,但不能简单地塞进一个已有十多年历史的 API 中。
Go 团队最终意识到:有些问题不是修复几个 Bug 就能解决的,而是需要重新设计 API。
于是,JSON v2 诞生了。
从 2020 到 2026:一场持续六年的重构
这项工作的历程,实际上比 Go 1.25 实验版发布要早得多。
我们可以从 Go 官方博客披露的信息中,还原出一条相对完整的时间线。

这条时间线里,有两个值得特别关注的人物。
Daniel Martí 是 encoding/json 的维护者之一。他最早从旧版 API 的设计局限出发,思考如何构建一个全新的版本。
Joe Tsai 则有另一条技术线索。在参与 Go Protobuf API 的工作后,他发现 protojson 不得不使用独立的 JSON 实现,因为旧版 encoding/json 无法同时满足更严格的 JSON 标准要求和高效流式处理需求。
这成为推动 JSON v2 的另一个重要契机。
两人最终走到一起,开始构建新的 JSON 实现。
随后,Roger Peppe、Chris Hines、Johan Brandhorst-Satzkorn、Damien Neil、Austin Clements、Michael Pratt 等人陆续参与设计评审、代码审查和回归测试。
值得注意的是,Go 官方特别强调,这项工作很大程度上由 Google 之外的开发者推动和贡献。
这也是 Go 开源项目协作模式的一个缩影。
真正的重构:把 JSON 处理拆成两个层次
如果说前两部分讲的是为什么要重写,以及重写经历了什么,那么这一部分才是 JSON v2 最重要的技术成果。
过去,很多开发者会把 JSON 序列化理解成一个简单的过程:
Go 对象转换成 JSON 字符串,或者反过来。
但从实现角度来看,这里面其实包含两个不同的问题:
- JSON 本身是否合法?如何解析 JSON 语法?
- JSON 数据应该如何映射到 Go 类型?
旧版 encoding/json 将这两部分逻辑交织在一起,导致 API 扩展、性能优化和行为调整都受到限制。
JSON v2 最重要的架构调整,就是将它们彻底分离。
第一层:encoding/json/jsontext
负责 JSON 语法层面的处理。
它不关心 Go 的 struct、反射或者类型映射,只负责 JSON 本身。
例如:
- 读取和写入 JSON Token。
- 解析和生成完整 JSON Value。
- 处理 JSON 编码、解码。
- 提供真正的流式处理能力。
第二层:encoding/json/v2
负责 JSON 与 Go 类型之间的语义映射。
例如:
- Go struct 如何映射到 JSON Object。
- Go slice 如何映射到 JSON Array。
- 如何处理自定义 Marshaler。
- 如何通过 Options 控制序列化行为。
两层架构如下:

这种分层带来的价值远不止代码结构更加清晰。
首先,语法层不再依赖 Go 的反射机制,可以作为独立的 JSON 处理组件使用。
其次,语义层不再需要自己承担所有底层 JSON 解析工作,能够更加灵活地实现类型映射。
更重要的是,双方可以独立演进。
这也是为什么 JSON v2 不仅仅是一次代码重写,而是一次底层架构的重新设计。
六年重构,还有一个更难的任务:兼容过去
对于一个新项目来说,重新设计 API 并不困难。
真正困难的是:如何让新 API 和旧 API 在同一个生态中长期共存?
Go 团队在 JSON v2 设计中做了一个非常关键的决定:
旧版 encoding/json 并不会被废弃,而是使用新版实现作为底层引擎。
换句话说,encoding/json 和 encoding/json/v2 并不是两套完全独立的 JSON 实现。
底层统一,行为分流。

这样设计有几个好处:
第一,避免维护两套实现。
未来修复底层 Bug、优化性能或者增加新能力,不需要分别维护两套 JSON 引擎。
第二,允许逐步迁移。
开发者可以通过 Options 选择不同的行为组合,不必一次性接受所有行为变化。
第三,让旧版也能受益。
例如,新版提供了 MarshalJSONTo 和 UnmarshalJSONFrom 接口。即使某个项目仍然通过 v1 调用 JSON 序列化,也可以在底层使用这些新接口。
这意味着,Go 团队没有要求整个生态立即迁移到 v2,而是尽可能降低迁移成本。
官方也明确表示:旧版 encoding/json 将继续得到支持,不会因为 v2 的发布而被废弃。
这其实是一个非常值得借鉴的工程决策。
重构基础设施,不应该把迁移成本简单地转嫁给使用者。
小结:六年之后,Go 团队交付的不只是一个 JSON 库
Go 1.27 中的 JSON v2,终于从实验性功能变成了正式标准库 API。
从结果来看,它解决了旧版长期存在的设计问题,也带来了更灵活的 API 和更好的性能表现。
根据官方公布的基准测试,v2 的 Marshal 性能总体与 v1 接近,而 Unmarshal 在部分场景下最高可以达到 10 倍性能提升。
不过,Go 团队也没有回避性能上的不足:部分场景仍然慢于 v1,后续 Go 1.28 还会继续优化。
对于开发者而言,现在并不需要急着把所有项目迁移到 v2。
Go 官方已经提供了专门的迁移指南,旧版 API 也会继续得到支持。
真正值得关注的,是这项工作背后的工程方法。
回顾这六年,Go 团队并没有因为旧 API 存在缺陷,就急于推倒重来。
他们先识别问题,再重新设计 API;先构建原型,再邀请社区参与;先通过实验让真实项目检验,再最终承诺稳定性;同时,还要考虑如何让新旧实现长期共存。
这是一种相当克制的工程演进方式。
尤其是在今天,AI 让代码生成和重构的速度越来越快,写出一个新的 JSON 库或许并不困难。
但如何保证它能被数百万个 Go 项目安全使用十年甚至更久?
这才是真正困难的事情。
六年时间,Go 团队重写的不只是 JSON 库,更是为一个成熟生态重新设计基础设施的方式。
而这,或许才是 Go JSON v2 留给开发者最值得思考的东西。
参考资料:
- A new Go API for JSON - https://go.dev/blog/jsonv2
- A new experimental Go API for JSON - https://go.dev/blog/jsonv2-exp
- encoding/json/v2 Migration Guide - https://go.dev/doc/jsonv2-migration
- Go JSON v2 — Hacker News 讨论 - https://news.ycombinator.com/item?id=50369106
- Go 1.27 Release Notes - https://go.dev/doc/go1.27
还在为写 Agent 框架频频死循环、上下文爆炸而束手无策?我的新专栏 《从0 开始构建 Agent Harness》 将带你:
- 抛弃臃肿框架,回归“驾驭工程 (Harness Engineering)”的第一性原理
- 用 Go 语言手写 ReAct 循环、并发拦截与上下文压缩引擎等,复刻极简OpenClaw
- 构建坚不可摧的 Safety Middleware 与飞书人工审批防线
- 在底层实现 Token 成本审计、链路追踪与自动化跑分评估
- 从“调包侠”进化为掌控大模型边界的“AI 操作系统架构师”
扫描下方二维码,开启从 0 开始构建Agent Harness 的实战之旅。

你的Go技能,是否也卡在了“熟练”到“精通”的瓶颈期?
- 想写出更地道、更健壮的Go代码,却总在细节上踩坑?
- 渴望提升软件设计能力,驾驭复杂Go项目却缺乏章法?
- 想打造生产级的Go服务,却在工程化实践中屡屡受挫?
继《Go语言第一课》后,我的《Go语言进阶课》终于在极客时间与大家见面了!
我的全新极客时间专栏 《Tony Bai·Go语言进阶课》就是为这样的你量身打造!30+讲硬核内容,带你夯实语法认知,提升设计思维,锻造工程实践能力,更有实战项目串讲。
目标只有一个:助你完成从“Go熟练工”到“Go专家”的蜕变! 现在就加入,让你的Go技能再上一个新台阶!

商务合作方式:撰稿、出书、培训、在线课程、合伙创业、咨询、广告合作。如有需求,请扫描下方公众号二维码,与我私信联系。





