编程 Go 1.27 的 encoding/json/v2 与 jsontext:默认行为变化与迁移顺序

2026-10-08 21:02:05

Go 1.27 的 encoding/json/v2 与 jsontext:默认行为变化与迁移顺序

Go 1.27 新增两个包:encoding/json/v2 和 encoding/json/jsontext。

两个包各自负责什么

encoding/json/v2 提供 Marshal、MarshalWrite、MarshalEncode、Unmarshal、UnmarshalRead、UnmarshalDecode,均接受可变参数 Options 来配置序列化/反序列化行为。其中 MarshalWrite 直接写 io.Writer,UnmarshalRead 直接读 io.Reader,不需要先构造 Encoder/Decoder。

encoding/json/jsontext 做 JSON 语法层处理,不依赖反射。Encoder/Decoder 操作 Token 和 Value 序列,内部维护状态机验证 JSON 合法性。Decoder 有 ReadToken、ReadValue、PeekKind、SkipValue、StackDepth、StackPointer 等方法。

v2 相对 v1 的默认行为差异

v2 默认更严格、更可互操作,几处差异会直接影响现网数据:

  • 无效 UTF-8:v2 拒绝 JSON 字符串中的无效 UTF-8。v1 默认把无效字节替换为 Unicode 替换字符 U+FFFD,这实际上是数据损坏。需要旧行为时用 jsontext.AllowInvalidUTF8 改回。
  • 重复名字:v2 拒绝 JSON 对象中的重复名字,v1 允许。可用 jsontext.AllowDuplicateNames 改回;允许时按观察顺序处理,后面的值替换或合并前面的值。
  • 名字匹配:v2 默认大小写敏感,v1 大小写不敏感。
  • 大整数精度:两者都可对具体整数类型保精度;反序列化到 any 接口时默认用 float64,可配置保精度。

encoding/json 现在由 v2 实现

encoding/json 包现由 v2 实现,序列化/反序列化行为保留,但错误信息文本可能变化。v1 API 继续支持,不强制迁移。v1 也新增了一批 Options,可以令 v2 以 v1 语义运行。

若遇到兼容问题,可用 GOEXPERIMENT=nojsonv2 在构建时禁用,恢复原 v1 实现。该 opt-out 预计未来会移除。

流式接口:MarshalJSONTo / UnmarshalJSONFrom

v2 引入 MarshalJSONTo / UnmarshalJSONFrom 接口方法,直接操作 Encoder/Decoder,纯流式处理,用来解决 v1 的 MarshalJSON/UnmarshalJSON 的性能问题。

迁移路径

调用 Marshal/Unmarshal 时传 DefaultOptionsV1,行为与 v1 完全相同。因此第一步可以安全地把所有调用改成 v2 + DefaultOptionsV1。

差异排查可以用 github.com/go-json-experiment/jsonsplit 这个包装包,在生产环境报告 v1/v2 差异:

  • CallBothButReturnV1:同时跑两遍,报告差异但返回 v1 的值;
  • AutoDetectOptions:自动定位引发差异的具体选项。

差异收敛稳定后,再切到 OnlyCallV2 或 CallBothButReturnV2。

从这个顺序看,取舍主要落在两处:一是 v2 的严格校验(无效 UTF-8、重复名字、大小写敏感)要不要接受,还是用对应的 Options 退回旧语义;二是错误文本变化,如果代码或测试依赖错误字符串,需要一并调整。GOEXPERIMENT=nojsonv2 只作为构建期的临时退路。

性能

Marshal 总体与之前持平,Unmarshal 明显更快。

背景

GOEXPERIMENT 期间移除过 format 标签、unknown 标签、DiscardUnknownMembers、SkipFunc;inline 标签改名为 embed。

参考

  • 迁移文档:
  • encoding/json/v2:
  • encoding/json/jsontext:
  • 实验仓库:
  • 提案:
复制全文 生成海报 Go encoding json jsontext JSON 迁移

推荐文章

程序员茄子在线接单