9、encoding、json
9、encoding/json
序列化:Go 值 → JSON
使用 json.Marshal 或 json.MarshalIndent 将 Go 的数据类型转换为 JSON 字节切片。
基础用法
import "encoding/json"
type Person struct {
Name string
Age int
}
p := Person{"Alice", 30}
data, _ := json.Marshal(p)
fmt.Println(string(data)) // {"Name":"Alice","Age":30}
// 带缩进
data, _ = json.MarshalIndent(p, "", " ")
/*
{
"Name": "Alice",
"Age": 30
}
*/
序列化规则(无 tag)
- 只有 导出字段(首字母大写) 才会被序列化。
- JSON 键名默认与字段名相同(区分大小写)。
结构体标签 json:"..."
标签格式:json:"键名,选项"
选项可组合(如 json:"age,omitempty,string")。
| 选项 | 说明 | 示例 |
|---|---|---|
myName |
自定义 JSON 键名 | json:"user_name" |
- |
忽略该字段(不序列化也不反序列化) | json:"-" |
omitempty |
零值时忽略字段(false, 0, 空指针, 空字符串等) | json:"title,omitempty" |
string |
将数字转为 JSON 字符串(常用于 int64 精度问题) | json:"id,string" |
inline |
将匿名字段(或标记了inline 的字段)的字段内联到父级,展平嵌套结构 |
inline |
示例:
type User struct {
ID int64 `json:"id,string"` // 输出为 "123"
Name string `json:"name"`
Password string `json:"-"` // 忽略
Age int `json:"age,omitempty"` // 0 时不输出
Email string `json:"email,omitempty"` // 空时不输出
}
输出:
{"id":"123","name":"Alice"}
嵌套结构体与匿名字段:
- 匿名(嵌入)字段的字段会被提升,就像直接定义在外层一样(方法提升类似)。
- 若外层有同名字段,会遮蔽内层;可通过显式路径访问内层字段。
- 匿名字段也可带标签:
json:"-"会阻止提升。
反序列化:JSON → Go 值
使用 json.Unmarshal 将 JSON 字节切片解析到指定的变量(通常是指向结构体的指针)。
基础用法
data := []byte(`{"Name":"Bob","Age":25}`)
var p Person
err := json.Unmarshal(data, &p)
字段匹配规则
- 首先查找 JSON 键与结构体标签中
json名完全匹配的导出字段。 - 若无标签,则按大小写不敏感匹配字段名。
- 未匹配的 JSON 键被忽略;未匹配的结构体字段保持零值。
- JSON 中的
null会设置对应字段为 Go 的零值(比如*int设为 nil)。
类型兼容
- JSON 数字可解析为
float64,但若目标字段是int/int64等,必须保证 JSON 数字是整数(无小数部分),否则报错。 string标签的字段要求 JSON 中对应值为字符串,内部再转为数字。
处理未知字段
- 使用
map[string]interface{}接收所有键值,然后进行类型断言。 - 利用
json.RawMessage延迟解析部分字段。
示例:
var result map[string]interface{}
json.Unmarshal(data, &result)
name := result["name"].(string)
解析复杂嵌套 JSON
通过结构体精确建模
定义多层嵌套结构体,与 JSON 结构一一对应。
JSON 示例:
{
"company": "Acme",
"employees": [
{
"name": "Alice",
"role": "engineer",
"skills": ["Go", "Kubernetes"]
},
{
"name": "Bob",
"role": "manager",
"skills": ["PM", "Agile"]
}
],
"address": {
"city": "New York",
"zip": "10001"
}
}
Go 结构体定义:
type Company struct {
Name string `json:"company"`
Employees []Employee `json:"employees"`
Address Address `json:"address"`
}
type Employee struct {
Name string `json:"name"`
Role string `json:"role"`
Skills []string `json:"skills"`
}
type Address struct {
City string `json:"city"`
Zip string `json:"zip"`
}
解析与输出:
var c Company
err := json.Unmarshal(jsonData, &c)
if err != nil { ... }
fmt.Printf("Company: %s, Employees: %d\n", c.Name, len(c.Employees))
// 遍历输出
for _, e := range c.Employees {
fmt.Printf("- %s (%s): %v\n", e.Name, e.Role, e.Skills)
}
使用 json.RawMessage 处理不一致的结构
当某个字段的类型可能变化,或需要延迟解析时,使用 json.RawMessage 暂存原始 JSON 数据。
type Flexible struct {
Kind string `json:"kind"`
Data json.RawMessage `json:"data"` // 先不解析
}
// 根据 Kind 再解析 Data 到不同结构体
var f Flexible
json.Unmarshal(input, &f)
if f.Kind == "typeA" {
var a TypeA
json.Unmarshal(f.Data, &a)
}
使用 interface{} 和类型断言解析未知结构
var anything interface{}
json.Unmarshal(data, &anything)
// 判断顶层是 map[string]interface{} 还是 []interface{}
m := anything.(map[string]interface{})
// 逐层断言...
但这种方法冗长且易错,推荐用结构体或 RawMessage。
核心要点速记
- 序列化:
json.Marshal,只序列化导出字段,标签控制键名和行为。 - 反序列化:
json.Unmarshal,字段匹配大小写不敏感,优先标签名。 - 标签选项:
omitempty、-、string、重命名,组合使用。 - 嵌套 JSON:定义匹配的结构体层级,切片表示数组,结构体表示对象。
- 动态结构:
json.RawMessage延迟解析,interface{}走类型断言。
浙公网安备 33010602011771号