介绍 这是一个简单的数据交换格式.近15年前,我们写了关于 ,这引入了将Go类型向和从JSON数据进行序列化和 deserialize的能力。自那时以来,JSON已成为互联网上使用的最流行的数据格式。 JavaScript 对象标记(JSON) 支持 JSON in Go 随着时间的推移,包随用户的需求而演变,并 这个博客文章是关于Go 1.25的新实验 和 这篇文章提出了一个新的主要API版本,提供了新包的概述,并解释了如何使用它。 encoding/json encoding/json/v2 encoding/json/jsontext 问题与 encoding/json 编码 / JSON Overall, 马歇尔和无马歇尔任意Go类型的想法,在JSON中有一些默认表示,加上自定义表示的能力,已被证明是高度灵活的。 encoding/json 行为错误 存在各种行为错误 : encoding/json : Over the years, JSON has seen increased standardization in order for programs to properly communicate. Generally, decoders have become stricter at rejecting ambiguous inputs, to reduce the chance that two implementations will have different (successful) interpretations of a particular JSON value. Imprecise handling of JSON syntax currently accepts invalid UTF-8, whereas the latest Internet Standard (RFC 8259) requires valid UTF-8. The default behavior should report an error in the presence of invalid UTF-8, instead of introducing silent data corruption, which may cause problems downstream. encoding/json currently accepts objects with duplicate member names. RFC 8259 does not specify how to handle duplicate names, so an implementation is free to choose an arbitrary value, merge the values, discard the values, or report an error. The presence of a duplicate name results in a JSON value without a universally agreed upon meaning. This could be and has been exploited before (as in ). The default behavior should err on the side of safety and reject duplicate names. encoding/json exploited by attackers in security applications CVE-2017-12635 JSON 通常用于与使用 JSON 实现程序的程序进行通信,这些实现程序不允许 null 被解析成一个数据类型,预计将是一个 JSON 数组或对象。由于编码/json 将一个 nil 数组或地图解析为 JSON 数组或地图,这可能导致在其他实现程序解析时出现错误。 Case-insensitive unmarshaling:当 unmarshaling时,一个 JSON 对象成员名被解决为一个 Go struct 字段名称,使用一个 case-insensitive 匹配。 不一致的方法调用:由于实施细节,MarshalJSON方法在指针接收器上声明不一致地被编码/json称。 火灾缺陷 火焰的 它可以是棘手或限制性的: encoding/json 用户经常写 json.NewDecoder(r).Decode(v),在输入的末尾无法拒绝追踪垃圾。 选项可以设置在 Encoder 和 Decoder 类型上,但不能与 Marshal 和 Unmarshal 函数一起使用。类似地,执行 Marshaler 和 Unmarshaler 接口的类型无法使用这些选项,并且没有办法将选项导入调用堆栈。 Compact、Indent 和 HTMLEscape 函数写成 bytes.Buffer,而不是像 []byte 或 io.Writer 这样的更灵活的函数。 绩效限制 将内部实施细节置于一边,公共 API 承诺对其进行某些性能限制: : The interface method forces the implementation to allocate the returned . Also, the semantics require that verify that the result is valid JSON and also to reformat it to match the specified indentation. MarshalJSON MarshalJSON []byte encoding/json : The interface method requires that a complete JSON value be provided (without any trailing data). This forces to parse the JSON value to be unmarshaled in its entirety to determine where it ends before it can call . Afterwards, the method itself must parse the provided JSON value again. UnmarshalJSON UnmarshalJSON encoding/json UnmarshalJSON UnmarshalJSON : Even though the and types operate on an or , they buffer the entire JSON value in memory. The method for reading individual tokens is allocation-heavy and there is no corresponding API for writing tokens. Lack of streaming Encoder Decoder io.Writer io.Reader Decoder.Token 此外,如果实施A 或 回归方法称之为 或 功能,然后性能变成平方。 MarshalJSON UnmarshalJSON Marshal Unmarshal 试图修复 直接 编码 / JSON 编码 / JSON 引入一个新的,不兼容的主要版本的包是一种沉重的考虑,如果可能的话,我们应该尝试修复现有的包。 虽然添加新功能相对容易,但很难更改现有功能. 不幸的是,这些问题是现有API的固有后果,使得它们在现有 API 中几乎无法修复。 . Go 1 兼容性承诺 我们可以原则上宣布单独的名称,如 或 ,但这等于在同一包内创建一个平行名称空间。 (今后称为 ) ,我们可以在一个单独的框架内进行这些变化。 名空间相反 (今后称为 ). MarshalV2 UnmarshalV2 encoding/json/v2 v2 v2 encoding/json v1 Planning for encoding/json/v2 编码 / JSON / V2 一个新的主要版本的计划 在2020年底,由于无法解决现行方案中的问题,丹尼尔·马蒂(其中一位维护者) )首先编写了他的想法 package should look like. Separately, after previous work on the 乔·扎伊对此感到失望。 需要使用自定义的 JSON 实现,因为 was neither capable of adhering to the stricter JSON standard that the Protocol Buffer specification required, nor of efficiently serializing JSON in a streaming manner. encoding/json encoding/json 什么是假设 v2 Go API for 协议泡沫 the protojson encoding/json 相信JSON的更光明的未来既有益又有可能实现,丹尼尔和乔联合起来进行脑风暴。 和 (with the initial code being a polished version of the JSON serialization logic from the Go protobuf module). Over time, a few others (Roger Peppe, Chris Hines, Johan Brandhorst-Satzkorn, and Damien Neil) joined the effort by providing design review, code review, and regression testing. Many of the early discussions are publicly available in our 和 . v2 开始建造原型 记录会议 meeting notes 这个工作从一开始就一直是公开的,我们越来越多地参与了更广泛的Go社区,首先是 和 , 而且最近 as a Go experiment (available in Go 1.25) for wider-scale testing by all Go users. 戈菲尔谈话 2023年底发布的讨论 2025年初发布的正式提案 adopting encoding/json/v2 该 努力已经持续了5年,包含了许多贡献者的反馈,并从生产环境中使用中获得了宝贵的经验经验。 v2 It’s worth noting that it’s largely been developed and promoted by people not employed by Google, demonstrating that the Go project is a collaborative endeavor with a thriving global community dedicated to improving the Go ecosystem. Building on 编码 / JSON / JSON 文本 编码 / JSON / JSON 文本 Before discussing the API,我们首先介绍了实验性 package that lays the foundation for future improvements to JSON in Go. v2 encoding/json/jsontext JSON 序列化在 Go 中可以分为两个主要组件: 涉及基于语法处理JSON的语法功能,以及 定义 JSON 值和 Go 值之间的关系的语义功能。 我们使用术语“编码”和“解码”来描述语法功能,而术语“马歇尔”和“无马歇尔”来描述语法功能。 本图提供了此分离的概述。紫色块代表类型,而蓝色块代表函数或方法。箭头的方向大致表示数据流量。 包,包含仅涉及语法的功能,而上半部分,由 包,包含功能,将语义含义分配给由底部的一半处理的语法数据。 jsontext json/v2 基本的火焰 是如下: jsontext package jsontext type Encoder struct { ... } func NewEncoder(io.Writer, ...Options) *Encoder func (*Encoder) WriteValue(Value) error func (*Encoder) WriteToken(Token) error type Decoder struct { ... } func NewDecoder(io.Reader, ...Options) *Decoder func (*Decoder) ReadValue() (Value, error) func (*Decoder) ReadToken() (Token, error) type Kind byte type Value []byte func (Value) Kind() Kind type Token struct { ... } func (Token) Kind() Kind 该 包提供功能,以在语法层面与 JSON 交互,并从其名称中提取 JSON 数据的语法被称为 由于它仅在语法层面与 JSON 进行交互,因此它不依赖于 Go 反射。 jsontext RFC 8259 第2节 JSON-text 该 和 提供对 JSON 值和代币的编码和解码的支持。 这些都影响了编码和解码的特殊行为。 和 类型宣布在 新的类型在 避免混淆语法和语义之间的区别,并以真正的流媒体方式运作。 Encoder Decoder 接受变量选项 Encoder Decoder v1 jsontext JSON 值是一个完整的数据单位,在 Go 中表示为 . It is identical to 在 . JSON 值由一个或多个 JSON 代币组成。 类型与建设者和配件方法. 它是相似的 在 但旨在代表任意的JSON代币而没有分配。 一个名字 []byte RawMessage v1 透明 Token Token v1 解决与业绩相关的基本问题 和 对于接口方法,我们需要一种高效的方式来编码和解码JSON作为代币和值的流序列。 , we introduce the 和 interface methods that operate on an 或 , allowing the methods’ implementations to process JSON in a purely streaming manner. MarshalJSON UnmarshalJSON v2 MarshalJSONTo UnmarshalJSONFrom Encoder Decoder 因此,该 包不必负责验证或格式化由返回的 JSON 值 ,也不需要负责确定提供的 JSON 值的界限。 . These responsibilities belong to the 和 . json MarshalJSON UnmarshalJSON Encoder Decoder 介绍 encoding/json/v2 编码 / JSON / V2 Building on the package, we now introduce the experimental 它旨在修复上述问题,同时留给用户熟悉。 我们的目标是:使用 will operate 同样,如果直接迁移到 . jsontext encoding/json/v2 v1 v1 大部分 v2 在本文中,我们将主要涵盖高级 API。 对于如何使用它的示例,我们鼓励读者学习 包装或阅读 . v2 the examples in the v2 Anton Zhiyanov’s blog covering the topic 基本的火焰 is the following: v2 package json func Marshal(in any, opts ...Options) (out []byte, err error) func MarshalWrite(out io.Writer, in any, opts ...Options) error func MarshalEncode(out *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(in *jsontext.Decoder, out any, opts ...Options) error The 和 函数具有类似的签名 , but accept options to configure their behavior. The 和 functions directly operate on an 或 ,避免需要暂时建立一个 或 只为写作或阅读这些人。 Marshal Unmarshal v1 MarshalWrite UnmarshalRead io.Writer io.Reader Encoder Decoder 该 和 函数在A上运作 和 它实际上是前面提到的功能的基本实现。 , 选项是每个马歇尔和非马歇尔函数的第一类论点,大大扩大了灵活性和可配置性。 有有有有 在 不是本条所涵盖的。 MarshalEncode UnmarshalDecode jsontext.Encoder jsontext.Decoder v1 v2 多种选项可用 v2 类型特定的定制 类似 , allows types to define their own JSON representation by satisfying particular interfaces. v1 v2 type Marshaler interface { MarshalJSON() ([]byte, error) } type MarshalerTo interface { MarshalJSONTo(*jsontext.Encoder) error } type Unmarshaler interface { UnmarshalJSON([]byte) error } type UnmarshalerFrom interface { UnmarshalJSONFrom(*jsontext.Decoder) error } 该 和 接口是相同的那些在 . The new 和 接口允许一个类型以 JSON 来表示自己 或 这允许将选项转移到呼叫堆,因为选项可以通过 辅助方法在 或 . Marshaler Unmarshaler v1 MarshalerTo UnmarshalerFrom jsontext.Encoder jsontext.Decoder Options Encoder Decoder 看 举个例子,说明如何实现维护 JSON 对象成员排序的自定义类型。 该 OrderedObject Caller 特定的定制 In 是的,呼唤者 和 也可以为任何任意类型指定自定义 JSON 表示,其中调用器指定函数优于类型定义方法或特定类型的默认表示。 v2 Marshal Unmarshal func WithMarshalers(*Marshalers) Options type Marshalers struct { ... } func MarshalFunc[T any](fn func(T) ([]byte, error)) *Marshalers func MarshalToFunc[T any](fn func(*jsontext.Encoder, T) error) *Marshalers func WithUnmarshalers(*Unmarshalers) Options type Unmarshalers struct { ... } func UnmarshalFunc[T any](fn func([]byte, T) error) *Unmarshalers func UnmarshalFromFunc[T any](fn func(*jsontext.Decoder, T) error) *Unmarshalers 和 建立一个常规马歇尔,可以传递给一个 电话使用 和 support similar customization for . MarshalFunc MarshalToFunc Marshal WithMarshalers UnmarshalFunc UnmarshalFromFunc Unmarshal 示例显示该功能如何允许对所有 类型将被处理 包装 该 ProtoJSON proto.Message protojson Behavior differences 虽然 aims to behave the same as 他的行为改变了 解决问题在 最突出的是: v2 大部分 v1 以某种方式 v1 reports an error in the presence of invalid UTF-8. v2 reports an error if a JSON object contains a duplicate name. v2 marshals a nil Go slice or Go map as an empty JSON array or JSON object, respectively. v2 unmarshals a JSON object into a Go struct using a case-sensitive match from the JSON member name to the Go field name. v2 redefines the tag option to omit a field if it would have encoded as an “empty” JSON value (which are , , , and ). v2 omitempty null "" [] {} reports an error when trying to serialize a , which currently has , but provides options to let the caller decide. v2 time.Duration no default representation 对于大多数行为更改,有一个结构标签选项或调用器指定选项,可以配置该行为以运行 或 语义,甚至其他调用定义的行为。 为了更多信息。 v1 v2 “迁移到V2” 绩效优化 该 绩效的 它几乎是与 有时它略快一点,但有时它略慢一点。 绩效的 显著快于 ,与基准显示到10x的改进。 Marshal v2 v1 Unmarshal v2 v1 In order to obtain greater performance gains, existing implementations of and 迁移也需要实施 和 , so that they can benefit from processing JSON in a purely streaming manner. For example, recursive parsing of OpenAPI specifications in 某些方法在 Kubernetes 的特定服务中显著损害了性能(见 ) ,而转换为 通过大小顺序提高性能。 Marshaler Unmarshaler MarshalerTo UnmarshalerFrom UnmarshalJSON 古巴人 / 古巴人 / 古巴人 # 315 UnmarshalJSONFrom 有关更多信息,请参见 收藏家 go-json-experiment/jsonbench Retroactively improving encoding/json encoding/json We want to avoid two separate JSON implementations in the Go standard library, so it is critical that, under the hood, 它在实施方面 . v1 v2 这种方法有几个好处: 渐进迁移: v1 或 v2 中的 Marshal 和 Unmarshal 函数代表了一组根据 v1 或 v2 语义运作的默认行为. 可以指定配置 Marshal 或 Unmarshal 以完全 v1 运作,大多是 v1 与一些 v2 混合,大多是 v2 与一些 v1 或完全是 v2 语义。 功能继承:随着向后兼容的功能被添加到 v2,它们将本质上在 v1 中提供。例如,v2 增加了对多个新的结构标签选项的支持,例如 inline 或格式,以及对 MarshalJSONTo 和 UnmarshalJSONFrom 界面方法的支持,这些方法既具有更高的性能和灵活性。 减少维护: 维护一个广泛使用的包需要大量的努力. 通过使用 v1 和 v2 使用相同的实现,维护负担减少。 一般来说,单个更改会修复错误,提高性能,或为两种版本添加功能。 虽然选择了部分 may be deprecated over time (supposing 作为一个实验的毕业生),整个包将永远不会被剥夺。 将被鼓励,但不需要。Go项目不会放弃支持 . v1 v2 v2 v1 实验与 jsonv2 最新的火焰在 和 软件包默认情况下不可见. 若要使用它们,请使用 在您的环境中或与 实验的本质是API不稳定,并可能在未来发生变化.虽然API不稳定,但实现质量很高,已成功地被几个大型项目用于生产。 encoding/json/jsontext encoding/json/v2 GOEXPERIMENT=jsonv2 goexperiment.jsonv2 事实是, 它在实施方面 也就是说,基本的实施 is completely different when building under the experiment. Without changing any code, you should be able to run your tests under 理论上,没有什么新事物会失败: v1 v2 v1 jsonv2 jsonv2 GOEXPERIMENT=jsonv2 go test ./... 重新实施的 in terms of 其目的在于在其境界内提供相同的行为。 ,虽然有些差异可能可以观察到,例如错误消息的准确编写。 报告任何回归 . v1 v2 Go 1 兼容性承诺 jsonv2 on the issue tracker Becoming an experiment in Go 1.25 is a significant milestone on the road to formally adopting 和 进入标准图书馆,但其目的 您的反馈将决定我们的下一步步骤,以及这个实验的结果,这可能导致任何东西,从放弃努力,到采用Go 1.26作为稳定的包。 ,并帮助决定Go的未来。 encoding/json/jsontext encoding/json/v2 jsonv2 go.dev/issue/71497 乔·扎伊、丹尼尔·马蒂、约翰·勃兰德霍斯特-萨茨科恩、罗杰·佩普、克里斯·海因斯和达米恩·尼尔 This article is available on under a CC BY 4.0 DEED license. 《Go》博客 《Go》博客 The Go Blog Photo by on Jr Korpa Unsplash JR 军队 Unsplash