编程 Go 1.27 的 encoding/json/v2:默认拒重复键和非法 UTF-8,v1 代码已换实现

2026-11-05 21:06:05

Go 1.27 的 encoding/json/v2:默认更严,存量服务三条迁移路径

Go 1.27 引入两个新包:encoding/json/v2 和 encoding/json/jsontext。原有的 encoding/json 没有废弃,而是换成了 v2 实现。

两个新包的 API 形态

encoding/json/v2 是 encoding/json 的大改版,提供六个入口,全部接受可变参数 Options:

func Marshal(in any, opts ...Options) ([]byte, error)
func MarshalWrite(out io.Writer, in any, opts ...Options) error
func MarshalEncode(enc *jsontext.Encoder, in any, opts ...Options) error

func Unmarshal(in []byte, out any, opts ...Options) error
func UnmarshalRead(in io.Reader, out any, opts ...Options) error
func UnmarshalDecode(dec *jsontext.Decoder, out any, opts ...Options) error

encoding/json/jsontext 做更低层的语法处理。Encoder 和 Decoder 把 JSON 当作 Token 与 Value 的序列操作,内部维护状态机,保证产出或消费的序列是合法 JSON 文本。

v2 的默认行为比 v1 更严格

为了互操作性,v2 默认:

  • 拒绝 JSON 字符串中的非法 UTF-8;
  • 拒绝 JSON 对象内的重复成员名。

完整的差异清单和对应的调整选项在 v1 encoding/json 包文档里。

encoding/json 已由 v2 实现

encoding/json 的编组和解组行为保持不变,但错误信息的具体文本可能不同。这一点对做了错误字符串匹配的服务是实际风险,测试里的 err.Error() 断言需要复查。

性能方面:Marshal 大致与旧实现持平,Unmarshal 显著更快。

包内还新增了一批 Options,可以把 v2 配置成 v1 语义,从而不必一次性完成 API 迁移。v1 API 会继续支持,不强制迁移。

迁移取舍

三条路径,按改动成本排序:

  1. 完全不动:继续用 v1 API。行为保留,只需确认没有依赖错误文本。
  2. 用 Options 调回 v1 语义:仍走 v1 API,但通过新增的 Options 对齐历史行为。适合暂时不想改调用点、又需要 v2 性能或逐渐收紧校验的场景。
  3. 迁到 v2 API:直接使用 Marshal/Unmarshal 系列函数,显式传入 Options 表达策略。改动面最大,但语义最清楚。

如果新实现造成兼容问题,可以在构建时设置 GOEXPERIMENT=nojsonv2,恢复到原来的 v1 实现。这个回退开关预计在后续版本移除。

GOEXPERIMENT 期间被移除的项

从 GOEXPERIMENT 阶段到现在,encoding/json/v2 有几处删除:

  • format 标签选项(#79071)
  • unknown 标签选项(#77271)
  • DiscardUnknownMembers 编组选项(#77271)
  • SkipFunc 哨兵错误(#74324)

同时有两处改名和行为调整:

  • inline 标签选项改名为 embed(#79985)
  • string 标签选项(#79065)与 MatchCaseInsensitiveNames 选项(CL 792780)的行为有更新

在 encoding/json/jsontext 中,数值类型的 Token 访问器改为同时返回错误(#77666)。原先直接取值的地方需要处理新增的 error。

背景和更多细节见 proposal issue;需要关闭新实现时,请提交 issue。

Go 博客索引中对应文章为 “A new Go API for JSON, 4 November 2026”。

复制全文 生成海报 Go JSON 标准库 迁移 后端

推荐文章

程序员茄子在线接单