跳到主要内容
代码工具2026-08-284 分钟阅读

如何把 JSON 快速转成 Go struct(含 json tag 与嵌套类型)

对接第三方 API,返回一大段 JSON。你不想手写几十个字段的 Go struct——既慢又容易拼错 tag。能不能自动转?

能。下面讲清楚 JSON → Go struct 的类型映射规则,以及怎么一键生成带 json tag 的代码。

JSON 类型到 Go 类型的映射

JSON 类型Go 类型备注
对象 {}struct {…}嵌套结构体
数组 []切片 []T元素类型取数组内容推断
字符串 "a"string
数字(无小数)int64 / float64取决于是否有小数点
数字(有小数)float64
布尔 true/falsebool
null指针或 interface{}建议用指针 *T 区分「零值」与「未设置」
混合类型数组[]interface{}尽量避免

关键坑:数字与 null

Go 没有「可空的基础类型」。如果字段可能是 null,直接写成 int 反序列化会报错,应写成指针:

type User struct {
    Name  string  `json:"name"`
    Age   *int    `json:"age"`   // 可能为 null
    Score *float64 `json:"score"`
}

一步步生成(无需装任何插件)

用本站 JSON 转 Go 工具,在浏览器本地完成,JSON 不上传:

  1. 打开 JSON 转 Go。
  2. 粘贴你的 JSON(可以是对象或数组)。
  3. 配置选项:
    • JSON Tag:默认 保留原始键名,让 json:"…" 与样例线上键一致;只有故意改写线上形态时才选 snake_case / camelCase;无 json 标签则不生成 tag。
    • 导出样式:Go 字段用 PascalCase 或 camelCase(导出字段名仍会首字母大写,以便 encoding/json 可见)。
    • 根类型名(如 Response、User)以及可选的 omitempty。
  4. 输入变化时输出即时更新——把 struct 代码复制进项目即可。
  5. 如果还要转成其他语言,可再用 JSON 转 TypeScript 或 JSON 转 Java。

一个完整例子

输入 JSON:

{
  "id": 1024,
  "name": "Alice",
  "isActive": true,
  "tags": ["go", "api"],
  "profile": {
    "age": 30,
    "city": "Shanghai"
  }
}

工具生成的 Go 代码:

type Profile struct {
    Age  int    `json:"age"`
    City string `json:"city"`
}

type Response struct {
    ID       int      `json:"id"`
    Name     string   `json:"name"`
    IsActive bool     `json:"isActive"`
    Tags     []string `json:"tags"`
    Profile  Profile  `json:"profile"`
}

进阶建议

  • 先格式化再转换:杂乱的 JSON 先用 JSON 格式化工具 整理,类型推断更准。
  • 区分零值与缺失:对外接口字段多用指针,避免把「未返回」误判为「0 / 空串」。
  • 时间字段:JSON 里的时间通常是 RFC3339 字符串,Go 里用 time.Time 并确认 json tag 与格式一致。
  • 大写导出:Go 字段必须大写才能被 encoding/json 序列化,转换工具默认已处理,手写时别漏。

常见问题

Q:数组里元素类型不一致怎么办? 转成 []interface{},你自己再断言。若能改接口,尽量让元素结构统一。

Q:数字有时是整数有时是小数? 不确定时用 float64;若确定是整数,手动改成 int64。

Q:嵌套太深,生成的 struct 太多? 可以考虑用 map[string]interface{} 临时接收,再按需定义结构体,或分层转换。

Q:要生成带 omitempty 的 tag 吗? 视场景。写请求体时 omitempty 能省掉零值字段;读响应体时通常不需要,避免把「缺失」当成「零值」。

Go 三坑:时间、null、接口

time.Time 序列化为 RFC 3339 字符串,反序列化只认这个格式——API 吐 epoch 毫秒(JS 默认)就要自定义类型或 int64 加转换。null 进 string 字段静默变空串(无报错、遗漏式数据损坏);进 *string 才是正确的 nil——API 里每个可空字段都该生成指针。interface:异构数组把类型开关推进每个消费者;更干净的模式是两个显式字段配 json.RawMessage 按需解码。

json tag 必须贴合线上键(而不是迁就 Go 审美)

典型踩坑:粘贴 camelCase JSON(userName),却把 tag 下拉设成 snake_case。旧默认会把 tag 改写成 user_name,json.Unmarshal 于是永远填零值——编译不报错,只是静默丢数据。ToolVault 现在默认 保留原始键名:tag 字符串等于样例里的 JSON 键。只有在你故意发射另一种约定时(例如 Go 字段保持惯用写法,但请求体必须是 snake),才用 snake/camel 改写。

样例里的大整数走无损路径(无小数点时倾向 int64);若中间又经过别的编辑器,仍要核对是否超过 JS 安全整数。

值得手工加的三个 tag

omitempty 在可选字段上——否则响应回显零值(count:0)让消费者分不清「值为零」和「字段缺席」;,string 加在遗留 API 按字符串发数字的字段上(JS 大数恐惧症的产物);命名分离——Go 惯例要 UserID 而 JSON 说 userId,让 tag 承担不匹配,不要为此起非惯用 Go 名。版本化 API 每版一套 struct,好过 interface 汤。

往返测试生成的类型

能编译的 struct 不等于能往返:把原始 JSON 过 json.Unmarshal 再 json.Marshal,规范化键序后字节比对。差异精确出现在 schema 与 Go 分歧处——null 处理、数字精度(float64 静默吞大 int64——ID 用 json.Number)、未知字段丢弃。这十行测试属于每个带 schema 的服务的 CI。

小结

JSON 转 Go struct = 按类型映射规则生成带 json tag 的结构体,注意 null 用指针、数字类型要选对。用本站的 JSON 转 Go 工具 在本地一键生成,JSON 不上传,更适合处理含敏感字段的接口数据。

实现注记

同站 JSON 转 TypeScript 工具修过的一个坑对类型生成都有参考价值:异构数组曾静默丢弃第一个元素之后的内容,后来改为产出联合元素类型,元素类型名按单数化键名生成(users → User)。Go 没有 union 可退,给异构数组建 struct 更要在样例里多放几条数据,别让第一条替整批表态。