JSON转Go结构体
从JSON生成Go结构体
JSON输入
Go输出
什么是JSON转Go结构体转换?
JSON转Go结构体转换是指将原始JSON数据转换为与Go标准库encoding/json包配合使用的类型定义。Go是静态类型语言,每个JSON字段都需要一个对应的结构体字段,包含正确的类型以及告知序列化器如何映射JSON键的结构体标签。对于大型或深度嵌套的API响应,手动编写这些定义既耗时又容易出错。
自Go 1.0起纳入规范的encoding/json包使用反射将JSON键与导出的结构体字段进行匹配。默认情况下匹配不区分大小写,但显式json结构体标签是标准做法,因为它消除了歧义,并允许Go使用PascalCase命名规范,同时保持JSON为camelCase或snake_case。转换器自动完成这一过程:读取JSON,从值推断Go类型,并输出带有正确标签的结构体定义。
Go的类型系统与JSON能够清晰对应。字符串映射为string,布尔值映射为bool,整数映射为int,浮点数映射为float64。嵌套JSON对象映射为独立的命名结构体,数组映射为切片。唯一的差距在于null:Go没有通用的可空类型,因此null值通常映射为指针类型(*string、*int)或interface{}。生成器在毫秒内处理所有这些情况。
为什么使用JSON转Go生成器?
手动根据JSON定义Go结构体意味着要数清括号、猜测类型,以及每次API变更时重写标签。生成器消除了这些摩擦。
JSON转Go结构体使用场景
JSON到Go类型映射参考
encoding/json包在将JSON值映射到Go类型时遵循特定规则。下表展示默认映射和常见替代方案。「默认」列是大多数生成器的输出。「替代」列展示可根据需求选择的类型,例如对大型ID使用int64,对可空字段使用指针类型。
| JSON类型 | 示例 | 默认 | 替代 |
|---|---|---|---|
| string | "hello" | string | string |
| number (integer) | 42 | int | int64 |
| number (float) | 3.14 | float64 | float64 |
| boolean | true | bool | bool |
| null | null | interface{} | *string / *int |
| object | {"k": "v"} | struct | struct |
| array of strings | ["a", "b"] | []string | []string |
| array of objects | [{"id": 1}] | []Item | []Item |
| mixed array | [1, "a"] | []interface{} | []interface{} |
Go JSON结构体标签参考
结构体标签控制encoding/json如何序列化和反序列化字段。json标签是最常用的,但可以与其他标签(db、yaml、xml)组合使用于同一字段。标签语法是字段类型后面由反引号分隔的字符串。以下是encoding/json支持的json标签选项。
| 标签 | 行为 | 使用场景 |
|---|---|---|
| json:"name" | Maps struct field to JSON key "name" | Always generated |
| json:"name,omitempty" | Omits field from output if zero value | Optional fields |
| json:"-" | Field is never serialized or deserialized | Internal fields |
| json:"name,string" | Encodes int/bool as JSON string | String-encoded numbers |
json.Unmarshal与json.NewDecoder的区别
Go提供两种解码JSON的方式:json.Unmarshal处理字节切片,json.NewDecoder处理io.Reader流。两者使用相同的结构体标签规则,但适用场景不同。
代码示例
以下示例展示如何在真实程序中使用从JSON生成的Go结构体,以及如何从其他语言生成这些结构体。Go示例使用标准库中的encoding/json。
package main
import (
"encoding/json"
"fmt"
)
type User struct {
ID int `json:"id"`
Name string `json:"name"`
Email string `json:"email"`
IsActive bool `json:"is_active"`
Tags []string `json:"tags"`
}
func main() {
data := []byte(`{"id":1,"name":"Alice","email":"alice@example.com","is_active":true,"tags":["admin","editor"]}`)
var user User
if err := json.Unmarshal(data, &user); err != nil {
panic(err)
}
fmt.Println(user.Name) // → Alice
fmt.Println(user.Tags) // → [admin editor]
}// JSON response from API — this is the shape you'd convert to Go:
const res = await fetch("https://api.example.com/users/1");
const user = await res.json();
// user → { "id": 1, "name": "Alice", "email": "alice@example.com" }
// Equivalent Go struct:
// type User struct {
// ID int `json:"id"`
// Name string `json:"name"`
// Email string `json:"email"`
// }import json
def json_to_go(data: dict, name: str = "Root") -> str:
lines = [f"type {name} struct {{"]
type_map = {str: "string", int: "int", float: "float64", bool: "bool"}
for key, value in data.items():
go_type = type_map.get(type(value), "interface{}")
field = key.title().replace("_", "") # snake_case → PascalCase
lines.append(f'\t{field} {go_type} `json:"{key}"`')
lines.append("}")
return "\n".join(lines)
data = json.loads('{"user_name": "Alice", "age": 30, "verified": true}')
print(json_to_go(data))
# type Root struct {
# UserName string `json:"user_name"`
# Age int `json:"age"`
# Verified bool `json:"verified"`
# }package main
import (
"encoding/json"
"fmt"
"strings"
)
type Event struct {
Type string `json:"type"`
Payload string `json:"payload"`
}
func main() {
stream := strings.NewReader(`{"type":"click","payload":"btn-1"}
{"type":"scroll","payload":"page-2"}`)
dec := json.NewDecoder(stream)
for dec.More() {
var ev Event
if err := dec.Decode(&ev); err != nil {
break
}
fmt.Printf("%s: %s\n", ev.Type, ev.Payload)
}
// → click: btn-1
// → scroll: page-2
}