JSON → Go struct 转换器,免费

解析 JSON 并自动生成 Go struct 定义,字段名使用 PascalCase,并带 json 标签,可流畅集成到您的 Go 代码中。

输入与输出

选项

工作原理

  1. 粘贴您的 JSON:在输入区输入任意 JSON 对象或嵌套结构。
  2. 获得 Go struct:立即生成对应的 Go struct 定义,带有正确的字段名、类型和 JSON 标签。
  3. 复制并使用:复制 struct 代码,粘贴到 Go 项目中即可开始反序列化 JSON 数据。

为什么使用 JSON → Go struct 转换器?

为复杂的 JSON 响应手写 Go struct 既繁琐又容易出错,尤其是当 API 返回带有几十个字段的深层嵌套对象时。JSON → Go 通过分析 JSON 的形状自动完成此工作,生成类型正确的 Go struct,并附带与 JSON 字段名对应的 json:"…" 标签。这在消费 REST API、处理 webhook 载荷或使用任何 JSON 数据源时,能大大加快 Go 开发速度。

功能特性

  • 嵌套 struct 生成:深层嵌套的 JSON 对象会被转换为嵌套的 Go struct 定义。
  • 类型推断:字符串、数字、布尔值、数组和 null 值都会映射到合适的 Go 类型。
  • JSON 标签:所有字段都带有与原始 JSON 键匹配的 json:"fieldName" struct 标签。
  • 数组处理:JSON 数组转换为带正确元素类型的 Go slice。
  • 指针类型:可为空的 JSON 字段用指针类型表示(例如 *string)。

常见问题

null 值如何处理?

null 的 JSON 字段在 Go 中转换为指针类型(例如 *string、*int),这样可以同时表示 nil 和实际值。能正确处理 API 响应中的可选或可为空字段。

如果 JSON 类型不一致会怎样?

如果同一字段在不同 JSON 对象中以不同类型出现(在真实 API 中很常见),转换器可能会推断为 interface{}(现代 Go 中的 any)。请检查此类字段,并根据需要添加自定义类型断言。

会生成完整的反序列化代码吗?

工具生成 struct 定义。进行反序列化时,使用标准的 encoding/json 包:json.Unmarshal(data, &myStruct) 或 json.NewDecoder(r.Body).Decode(&myStruct)。

Go 与 JSON 简史

Go 由 Rob Pike、Robert Griesemer 和 Ken Thompson2009 年 11 月在 Google 发布,标准库 encoding/jsonGo 1.0(2012 年 3 月)之前已经稳定。Go 的 JSON 解析基于反射:声明结构体、用 json:«name» 标签标注字段、调用 json.Unmarshal 即可同时遍历结构体和字节流。这种模式效率很高,但为大型 API 手写结构体很快就变得繁琐。Matt Holt2014 年开源了 mholt/json-to-go(目前仍在 mholt.github.io/json-to-go 维护),完全运行在浏览器中,用正则解析 JSON 推断结构。quicktype(David Siegel,2017)将这一思路扩展到 23 种语言,包括 TypeScript、Rust、Swift、Kotlin、Python、Java。性能导向的 easyjson(Mail.ru,2016)通过编译期生成 marshalling 代码跳过反射,热路径上比 encoding/json 快 4-5 倍。Go 1.18(2022 年 3 月)引入泛型,简化了部分 marshalling 工具函数但没有改变 struct tag 习惯。Go 1.21(2023 年 8 月)加入了实验性的 encoding/json/v2 包(2024 年仍在讨论中),目标是修复长期存在的痛点,如静默的类型不匹配和反射性能问题。

结构体 vs map[string]interface{}:何时选用哪种

  • 已知形态优先使用结构体。当您控制 API 或拥有稳定 schema 时,结构体提供编译期字段检查、IDE 自动补全和 2-3 倍更快的解析。json.Unmarshal 解析到结构体时只处理它识别的字段,静默忽略未知键(除非调用 decoder.DisallowUnknownFields)。
  • 任意 JSON 优先使用 map。map[string]interface{}(Go 1.18+ 写作 map[string]any)适合形态多变的数据:多来源 webhook、插件配置、MongoDB 文档。代价:每次访问需要类型断言(data[«age»].(float64)),且数字默认解析为 float64,大整数会丢失精度。
  • 混合方案:已知字段用结构体,未知字段用 map。json.RawMessage 延迟解析,或加上 Extra map[string]interface{} `json:«-,inline»` 做兜底(借助 go-restful 等库)。Stripe 和 Twilio 的 Go SDK 用此模式保持 API 响应的向后兼容。
  • 性能差距。热路径(如每秒解析 10000 个 webhook 事件)受益于生成式 marshaller:easyjsonffjsongo-jsonsonic(字节跳动,2022,运行时用 JIT 生成机器码,是 Go 最快的 JSON 库)。基于反射的通用 struct 解析吞吐量约 100-200 MB/s;sonic 可达 1-2 GB/s。
  • 大 JSON 用流式处理。1 GB 的 JSON 文件无法整体载入内存。json.Decoder.Token() 按 token 读取。json.Decoder.More() 逐个遍历数组元素。流式适用于日志文件、大型数据集、ND-JSON 流(每行一个 JSON 对象,被 OpenSearch、BigQuery、ChatGPT API 使用)。

JSON 到 Go 转换的真实节省时间场景

  • REST API 集成。Stripe、GitHub、Slack、Notion 都返回深度嵌套的 JSON。把样例响应粘贴到 json-to-go 能产出 80-90% 的最终结构体。把节省下的时间花在剩下 10%:自定义时间格式 unmarshaller、omitempty 语义、可选指针字段。
  • Webhook 处理器。典型的 Stripe webhook payload 有 80-200 个字段、3-5 层嵌套。手写 Go 结构体需要 30-60 分钟;通过 json-to-go 粘贴样例只需 30 秒。
  • 配置文件解析。JSON 配置文件(AWS Lambda、Docker Compose 的 compose.json、devcontainer.json 都优先使用 JSON 而非 TOML)能干净地映射到 Go 结构体。encoding/json 用同一个 struct 同时处理读取和写入。
  • gRPC 与 Protobuf 桥接。生成的 Go protobuf 类型自带 json 标签。在 protobuf 和 JSON 之间转码时(gRPC-Gateway、Buf、Connect),json-to-go 帮助从 JSON wire 格式勾勒出 Go 侧的形状。
  • 数据库 JSONB 列。PostgreSQL jsonb、MongoDB 文档、MySQL 的 JSON 列。database/sql 驱动返回 []byte,您 unmarshal 到结构体。生成器加速 schema-on-read 表的首轮开发。
  • 测试 fixture 数据。真实的生产 JSON 响应成为测试 fixture。json-to-go 给出 unmarshal 用的结构体供断言。配合 testdata/ 目录(Go 自 1.10 起的惯例)让测试贴近真实。
  • CSV-到-JSON-到-结构体管道。数据工程任务:读 CSV,marshal 成 JSON 检查,粘贴到 json-to-go 拿到结构体,再写类型化管道。比从电子表格猜列类型快多了。

结构体生成后常见错误

  • 忽略 time.Time 处理。encoding/json 假设 RFC 3339 格式(2026-05-13T12:34:56Z)。多数 API 提供这种格式,但有些用 Unix 时间戳(Stripe)、无时区的 ISO(老 Microsoft API)、或自定义字符串。对于非 RFC-3339,定义带 UnmarshalJSON 方法的自定义类型。
  • float64 默认导致整数溢出。JSON 数字解析到 interface{} 时变成 float64;超过 2^53 的数字丢失精度。Stripe 客户 ID、Twitter snowflake、Discord 用户 ID(都是 64 位)需要 json.Number 或结构体中明确的 int64。安全解析到 interface{} 时使用 json.Decoder.UseNumber()
  • 误解 omitemptyomitempty 在 marshal 时如果是零值(空字符串、0、nil 指针、空 slice/map)则省略字段。它意味着「unmarshal 时可选」。值为 0 的 int 字段与缺失字段无法区分;如果您需要知道 API 是省略还是发送了零,使用 *int
  • 字段名大小写不匹配。Go 字段必须导出(大写)才能被 marshal。默认 JSON 名等于 Go 名字面量,所以 UserID 在 JSON 中变成 «UserID» 除非加标签。生成器添加 json:«userId» 标签桥接 Go 的 PascalCase 和 JSON 的 camelCase。忘记加标签意味着您的代码「自己跑通」但和外部 API 对接失败。
  • 过度使用指针类型。生成器有时默认用 *string*int 求安全。这导致每次访问都要 nil 检查。如果 API 保证字段总是存在(读 API 文档),用值类型省去间接。
  • 响应间类型不一致。有些 API 在一个响应中返回 tags: []string{«foo»},在另一个中返回 tags: «foo» 字符串。纯代码生成无法桥接这种差异。写自定义 UnmarshalJSON 处理两种情况,或退回 interface{} 并记录这种可变性。
  • 静默丢弃未知字段。默认情况下,json.Unmarshal 忽略不在结构体里的 JSON 键。这往往是 bug:API 新增了字段,您的代码不知道。在测试中用 decoder.DisallowUnknownFields() 抓住漂移;生产中保持宽松。

其他常见问题

本工具和经典的 mholt/json-to-go 有什么不同?

核心算法相同:解析 JSON、从每个字段的首次出现推断类型、生成带反引号标签字段的结构体。本实现完全在您的浏览器中运行,无分析、无网络调用;经典版托管在 mholt.github.io/json-to-go,同样仅浏览器但在不同域名。两者对 ~95% 的输入输出应一致;边缘情况(混合类型数组、深度嵌套匿名结构体)可能略有差异。如果需要精确的 mholt 输出,用那个;如果需要 JSON 永不离开设备的隐私保证,用本工具。

为什么有些字段生成为 interface{}?

三个常见原因。第一,源 JSON 中的 literal null:生成器无法从单独的 null 推断有用类型,因此回退到 interface{}。一旦您知道真实 schema,替换为类型化指针(*string*int)。第二,混合类型数组:[1, «two», true] 无法是类型化 slice。第三,样本中字段完全缺失,如有可能粘贴更具代表性的 JSON。在 Go 1.18+ 中,您可以把 interface{} 替换为更简洁的别名 any;两者在类型层等价。

我可以让 JSON 通过这个结构体往返而不丢数据吗?

通常可以,但有保留。encoding/json 保留字段值但不保留字段顺序(JSON 规范规定字段顺序无关紧要;Go 的 map 故意随机化)。数值精度:超过 2^53 的 int64 在 Go 的 int64 间往返安全,但如果转为 float64 或经过 JavaScript(如浏览器中转),会丢失数字。未知 JSON 字段在 unmarshal-再-marshal 时静默丢弃,如果往返保真重要,用 map[string]json.RawMessage 兜底保留。

我什么时候应该用生成的结构体代码(easyjson、sonic)而非 encoding/json?

默认用 encoding/json,对 95% 的服务够用且语言自带。当您做过性能剖析且 JSON marshalling 在 CPU flame graph 中占总时间 >10% 时切到 easyjsonsonic。典型切换点:每秒处理数千请求的 HTTP 服务、每小时摄入数 GB 的日志聚合器、实时流。两个替代方案保持相同的结构体定义和标签,所以可以无需改字段声明就切换。

我的 JSON 会被发送到服务器吗?

不会。JSON 解析和 Go 代码生成都在您设备的 JavaScript 中运行。粘贴时打开 DevTools 的 Network 面板,您会看到零外部请求。对私有 API 响应、客户数据 fixture、含 PII 的 webhook payload、以及任何 NDA 覆盖的内容都安全。