go-json-experiment/json

GitHub: go-json-experiment/json

这是一个实验性的 Go JSON 序列化库,旨在改进标准库 encoding/json 的性能、API 灵活性和行为规范。

Stars: 545 | Forks: 30

# JSON 序列化(v2) [![GoDev](https://img.shields.io/static/v1?label=godev&message=reference&color=00add8)](https://pkg.go.dev/github.com/go-json-experiment/json) [![构建状态](https://static.pigsec.cn/wp-content/uploads/repos/2026/05/db6aeaa4a5214859.svg)](https://github.com/go-json-experiment/json/actions) 本模块托管了 v2 `encoding/json` 的实验性实现。 该 API 不稳定,将定期进行破坏性更改。 请勿在公开可用的模块中依赖此包。 任何导致破坏性 API 或行为更改的提交,其提交信息顶部附近都会被标记为字符串 "WARNING: "。 升级该模块时,检查提交更改列表是您的责任。并非所有破坏性更改都会导致构建失败。 ## 当前状态 一个 [关于将此模块作为 `encoding/json/v2` 和 `encoding/json/jsontext` 纳入 Go 的提案](https://github.com/golang/go/issues/71497) 已于 2025-01-30 在 Go 的 GitHub 项目中启动。请在那里提供您的反馈。 目前,此模块作为 Go 实验在 Go 标准库中可用。 参阅 ["用于 JSON 的新实验性 Go API"](https://go.dev/blog/jsonv2-exp) 了解更多详情。 2025-11-20,Go 项目成立了一个 `json/v2` 工作组, 成员包括 @aclements、@neild、@prattmic、@ChrisHines 和 @dsnet, 旨在审查在准备正式纳入标准库过程中的未决问题。 工作组每周开会,会议记录在 [issue #76406](https://github.com/golang/go/issues/76406) 中。 参阅 [项目仪表板](https://github.com/orgs/golang/projects/50/views/1) 了解工作组需要解决的剩余问题。 "审查中" 和 "需要审查" 类别需要被清空 才能发布 `json/v2`。 在可预见的未来,此仓库将继续导入并镜像上游 Go 项目。 因此,代码更改应在上游 [Go 项目](https://go.googlesource.com/go/) 进行,而不是在此处。 使用 `goexperiment.jsonv2` 标签构建此模块将导致 该模块在底层使用 `encoding/json/v2` 包, 否则将提供一个不同的 `json/v2` 实现。 ## 目标和目的 * **大致向后兼容:** 如果可能,v2 应旨在在 API 和默认行为方面 与 v1 大致兼容,以简化迁移。 例如,`Marshal` 和 `Unmarshal` 函数是 v1 包中使用最广泛的 声明。v2 中的等效功能采用相同的名称并具有大致兼容的签名似乎是合理的。 在行为上,我们应力求 95% 到 99% 的向后兼容性。 我们不追求 100% 兼容,因为我们希望保留破坏某些现在被认为是错误行为的自由。 存在使 v2 实现达到 100% 兼容的选项, 但这不会是默认设置。 * **更灵活:** 有一个 [很长的功能请求列表](https://github.com/golang/go/issues?q=is%3Aissue+is%3Aopen+encoding%2Fjson+in%3Atitle)。 我们应旨在提供最灵活的功能,以满足大多数用例。 我们不希望过度适配 v2 API 来处理每一个可能的用例。 理想情况下,提供的功能本质上应是正交的,以便 任何功能组合都能产生尽可能少的令人惊讶的边缘情况。 * **更高效:** JSON 序列化被广泛使用,任何一点额外的 性能提升都将受到极大赞赏。v1 的一些不常用行为 可能会为了更好的性能而被舍弃。例如, 尽管 `Encoder` 和 `Decoder` 操作 `io.Writer` 和 `io.Reader`, 但它们并非以真正的流式方式运行, 导致了性能损失。v2 实现应默认旨在真正实现流式处理(参见 [#33714](https://golang.org/issue/33714))。 * **易于使用(难以误用):** v2 API 应旨在使 常见情况变得容易,不常见的情况至少成为可能。 API 应避免与用户预期相悖的行为, 这可能导致细微的错误(参见 [#36225](https://golang.org/issue/36225))。 * **v1 和 v2 的可维护性:** 由于 v1 实现必须永远保留, 如果 v1 能在底层使用 v2 实现将是有益的, 从而在未来减少维护负担。这可能意味着 v2 相对于 v1 的行为变化需要作为选项暴露出来。 * **避免使用 unsafe:** 标准库包通常避免使用 `unsafe` 包,即使它可能提供性能提升。 我们旨在保持这一特性。 ## 开发 此模块主要由 [@dsnet](https://github.com/dsnet)、 [@mvdan](https://github.com/mvdan) 和 [@johanbrandhorst](https://github.com/johanbrandhorst) 开发, 并由 [@rogpeppe](https://github.com/rogpeppe)、 [@ChrisHines](https://github.com/ChrisHines) 和 [@rsc](https://github.com/rsc) 提供反馈。 语义讨论定期举行, [过往会议记录可在此处找到](https://docs.google.com/document/d/1rovrOTd-wTawGMPPlPuKhwXaYBg9VszTXR9AQQL5LfI/edit?usp=sharing)。 ## 设计概览 此包旨在提供语法和语义之间的清晰分离。 语法处理 JSON 的结构表示(如 [RFC 4627](https://tools.ietf.org/html/rfc4627)、 [RFC 7159](https://tools.ietf.org/html/rfc7159)、 [RFC 7493](https://tools.ietf.org/html/rfc7493)、 [RFC 8259](https://tools.ietf.org/html/rfc8259) 和 [RFC 8785](https://tools.ietf.org/html/rfc8785) 所指定)。 语义处理语法数据作为可用应用程序数据的含义。 `Encoder` 和 `Decoder` 类型是流式分词器,关注 JSON 数据的 打包或解析。它们操作 `Token` 和 `Value` 类型, 这些类型代表了 JSON 中可表示的通用数据结构。 `Encoder` 和 `Decoder` 不旨在提供数据的任何解释。 像 `Marshal`、`MarshalWrite`、`MarshalEncode`、`Unmarshal`、 `UnmarshalRead` 和 `UnmarshalDecode` 这样的函数通过将 任意 Go 类型与其某种 JSON 表示形式(存储在 `[]byte`、`io.Writer`、`io.Reader`、`Encoder` 或 `Decoder` 等数据类型中)关联起来,提供语义意义。 ![API 概览](https://static.pigsec.cn/wp-content/uploads/repos/2026/05/b480342bdf214859.png) 此图提供了 v2 `json` 和 `jsontext` 包的高级概览。 紫色块代表类型,蓝色块代表函数或方法。 箭头及其方向代表数据的近似流向。 图的下半部分包含仅与语法相关的功能(由 `jsontext` 包实现), 而上半部分包含为语法数据(由下半部分处理) 赋予语义意义的功能(由 v2 `json` 包实现)。 与 v1 `encoding/json` 不同,选项被表示为独立的类型, 而不是 `Encoder` 或 `Decoder` 类型上的设置器方法。 一些选项在语法层影响 JSON 序列化, 而另一些在语义层影响它。 一些选项仅在解码时影响 JSON, 而另一些在编码时影响 JSON。 ## 行为变更 v2 `json` 包相对于 v1 `json` 包更改了 `Marshal` 和 `Unmarshal` 的默认行为, 使其更合理。 其中一些行为变更有选项和变通方法,以选择 与 v1 提供的行为类似的行为。 下表概述了这些变更: | v1 | v2 | 详情 | | -- | -- | ---- | | JSON 对象成员通过**不区分大小写的名称匹配**解组到 Go 结构体中。 | JSON 对象成员通过**区分大小写的名称匹配**解组到 Go 结构体中。 | [CaseSensitivity](/v1/diff_test.go#:~:text=TestCaseSensitivity) | | 当编组 Go 结构体时,标记为 `omitempty` 的结构体字段在**字段值是空的 Go 值**时会被省略,空值定义为 false、0、nil 指针、nil 接口值以及任何空数组、切片、映射或字符串。 | 当编组 Go 结构体时,标记为 `omitempty` 的结构体字段在**字段值会编码为空 JSON 值**时会被省略,空 JSON 值定义为 JSON null,或空的 JSON 字符串、对象或数组。 | [OmitEmptyOption](/v1/diff_test.go#:~:text=TestOmitEmptyOption) | | `string` 选项**会影响** Go 字符串和布尔值。 | `string` 选项**不会影响** Go 字符串或布尔值。 | [StringOption](/v1/diff_test.go#:~:text=TestStringOption) | | `string` 选项**不会递归影响** Go 字段值的子值。 | `string` 选项**会递归影响** Go 字段值的子值。 | [StringOption](/v1/diff_test.go#:~:text=TestStringOption) | | `string` 选项**有时接受**在 JSON 字符串中转义的 JSON null。 | `string` 选项**从不接受**在 JSON 字符串中转义的 JSON null。 | [StringOption](/v1/diff_test.go#:~:text=TestStringOption) | | nil Go 切片被编组为 **JSON null**。 | nil Go 切片被编组为 **空 JSON 数组**。 | [NilSlicesAndMaps](/v1/diff_test.go#:~:text=TestNilSlicesAndMaps) | | nil Go 映射被编组为 **JSON null**。 | nil Go 映射被编组为 **空 JSON 对象**。 | [NilSlicesAndMaps](/v1/diff_test.go#:~:text=TestNilSlicesAndMaps) | | Go 数组可以从**任意长度的 JSON 数组**解组。 | Go 数组必须从**相同长度的 JSON 数组**解组。 | [Arrays](/v1/diff_test.go#:~:text=Arrays) | | Go 字节数组表示为 **JSON 数字的 JSON 数组**。 | Go 字节数组表示为 **Base64 编码的 JSON 字符串**。 | [ByteArrays](/v1/diff_test.go#:~:text=TestByteArrays) | | 声明在指针接收者上的 `MarshalJSON` 和 `UnmarshalJSON` 方法**被不一致地调用**。 | 声明在指针接收者上的 `MarshalJSON` 和 `UnmarshalJSON` 方法**被一致地调用**。 | [PointerReceiver](/v1/diff_test.go#:~:text=TestPointerReceiver) | | Go 映射以**确定性顺序**编组。 | Go 映射以**非确定性顺序**编组。 | [MapDeterminism](/v1/diff_test.go#:~:text=TestMapDeterminism) | | JSON 字符串编码时**会转义 HTML 特定字符**。 | JSON 字符串编码时**不转义任何字符**(除非必要)。 | [EscapeHTML](/v1/diff_test.go#:~:text=TestEscapeHTML) | | 编组时,Go 字符串中的无效 UTF-8 **会被静默替换**。 | 编组时,Go 字符串中的无效 UTF-8 **会导致错误**。 | [InvalidUTF8](/v1/diff_test.go#:~:text=TestInvalidUTF8) | | 解组时,JSON 字符串中的无效 UTF-8 **会被静默替换**。 | 解组时,JSON 字符串中的无效 UTF-8 **会导致错误**。 | [InvalidUTF8](/v1/diff_test.go#:~:text=TestInvalidUTF8) | | 编组时,如果输出 JSON 值包含具有重复名称的对象,**不会发生错误**。 | 编组时,如果输出 JSON 值包含具有重复名称的对象,**会发生错误**。 | [DuplicateNames](/v1/diff_test.go#:~:text=TestDuplicateNames) | | 解组时,如果输入 JSON 值包含具有重复名称的对象,**不会发生错误**。 | 解组时,如果输入 JSON 值包含具有重复名称的对象,**会发生错误**。 | [DuplicateNames](/v1/diff_test.go#:~:text=TestDuplicateNames) | | 将 JSON null 解组到非空 Go 值时,**行为不一致,可能清除值或不执行任何操作**。 | 将 JSON null 解组到非空 Go 值时,**总是清除该值**。 | [MergeNull](/v1/diff_test.go#:~:text=TestMergeNull) | | 将 JSON 值解组到非空 Go 值时,**遵循不一致且奇怪的行为**。 | 将 JSON 值解组到非空 Go 值时,**如果输入是对象则总是合并,否则替换**。 | [MergeComposite](/v1/diff_test.go#:~:text=TestMergeComposite) | | `time.Duration` 表示为 **包含纳秒小数的 JSON 数字**。 | `time.Duration` 在 v2 中没有默认表示形式(参见 [#71631](https://golang.org/issue/71631)),会导致错误。 | | | 仅包含未导出字段的 Go 结构体**可以被序列化**。 | 仅包含未导出字段的 Go 结构体**不能被序列化**。 | [EmptyStructs](/v1/diff_test.go#:~:text=TestEmptyStructs) | 关于每一项变更的详情,请参阅 [diff_test.go](/v1/diff_test.go)。 ## 性能 v2 模块的目标之一是比 v1 更高效, 但不能以牺牲正确性为代价。 通常,v2 在编组性能上与 v1 持平, 但在解组方面显著更快。 参阅 https://github.com/go-json-experiment/jsonbench 获取基准测试结果, 该测试将 v2 与 v1 及其他多个流行的 JSON 实现进行了比较。
标签:API设计, Go模块, Go语言, JSON序列化, v2版本, 后端开发, 实验性模块, 序列化库, 数据格式, 日志审计, 标准库扩展, 程序破解, 编程工具, 软件开发, 远程代码执行