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:- 实验仓库:
- 提案: