JSON → Go struct 转换器,免费
解析 JSON 并自动生成 Go struct 定义,字段名使用 PascalCase,并带 json 标签,可流畅集成到您的 Go 代码中。
输入与输出
选项
工作原理
- 粘贴您的 JSON:在输入区输入任意 JSON 对象或嵌套结构。
- 获得 Go struct:立即生成对应的 Go struct 定义,带有正确的字段名、类型和 JSON 标签。
- 复制并使用:复制 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 Thompson 于 2009 年 11 月在 Google 发布,标准库 encoding/json 在 Go 1.0(2012 年 3 月)之前已经稳定。Go 的 JSON 解析基于反射:声明结构体、用 json:«name» 标签标注字段、调用 json.Unmarshal 即可同时遍历结构体和字节流。这种模式效率很高,但为大型 API 手写结构体很快就变得繁琐。Matt Holt 于 2014 年开源了 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:
easyjson、ffjson、go-json、sonic(字节跳动,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()。 - 误解
omitempty。omitempty在 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% 时切到 easyjson 或 sonic。典型切换点:每秒处理数千请求的 HTTP 服务、每小时摄入数 GB 的日志聚合器、实时流。两个替代方案保持相同的结构体定义和标签,所以可以无需改字段声明就切换。
我的 JSON 会被发送到服务器吗?
不会。JSON 解析和 Go 代码生成都在您设备的 JavaScript 中运行。粘贴时打开 DevTools 的 Network 面板,您会看到零外部请求。对私有 API 响应、客户数据 fixture、含 PII 的 webhook payload、以及任何 NDA 覆盖的内容都安全。