引入
http 包 处理 HTTP 请求时,数据交换最常用的格式就是 JSON。手动拼 JSON 字符串既容易出错又不优雅,Go 的 encoding/json 包让结构体和 JSON 之间的转换自动完成。
正文
定义
定义
encoding/json包提供 JSON 编码和解码 功能。Marshal把 Go 值序列化为 JSON 字节切片,Unmarshal把 JSON 字节切片反序列化为 Go 值。通过结构体标签控制字段映射关系。
语法
import "encoding/json"
// 编码:Go 值 → JSON
data, err := json.Marshal(v)
// 解码:JSON → Go 值
err = json.Unmarshal(data, &v)例子
Marshal:结构体转 JSON
type User struct {
Name string `json:"name"`
Age int `json:"age"`
}
u := User{Name: "Tom", Age: 20}
data, err := json.Marshal(u)
if err != nil {
log.Fatal(err)
}
fmt.Println(string(data)) // {"name":"Tom","age":20}json.Marshal 返回 []byte,用 string() 转换后就能看。结构体标签 `json:"name"` 指定 JSON 字段名。
Unmarshal:JSON 转结构体
type User struct {
Name string `json:"name"`
Age int `json:"age"`
}
jsonData := []byte(`{"name":"Tom","age":20}`)
var u User
err := json.Unmarshal(jsonData, &u)
if err != nil {
log.Fatal(err)
}
fmt.Println(u.Name, u.Age) // Tom 20json.Unmarshal 第一个参数是 JSON 字节切片,第二个是指针(&u)。JSON 字段名通过标签匹配到结构体字段。
结构体标签详解
type Product struct {
ID int `json:"id"` // 指定 JSON 字段名
Name string `json:"name,omitempty"` // 零值时省略
Price float64 `json:"-"` // 完全忽略此字段
internal string // 小写字段不会被编码
}json:"name"→ JSON 字段名为namejson:",omitempty"→ 零值时不输出(空字符串、0、nil、false)json:"-"→ 完全忽略,不编码也不解码- 首字母小写的字段默认不会被 JSON 包处理
MarshalIndent:格式化 JSON
u := User{Name: "Tom", Age: 20}
data, _ := json.MarshalIndent(u, "", " ")
fmt.Println(string(data))
// {
// "name": "Tom",
// "age": 20
// }MarshalIndent 生成带缩进的 JSON,第二个参数是前缀,第三个是缩进字符串。调试和写配置文件时很好用。
解码到 map
jsonData := []byte(`{"name":"Tom","age":20}`)
var m map[string]interface{}
json.Unmarshal(jsonData, &m)
fmt.Println(m["name"]) // Tom
fmt.Println(m["age"]) // 20(float64 类型)不知道 JSON 结构时,可以解码到 map[string]interface{}。注意 JSON 数字会解析为 float64。
json.Encoder 和 json.Decoder
// 写入 HTTP 响应
w.Header().Set("Content-Type", "application/json")
encoder := json.NewEncoder(w)
encoder.Encode(user)
// 从 HTTP 请求体读取
var u User
decoder := json.NewDecoder(r.Body)
decoder.Decode(&u)Encoder / Decoder 直接对接 io.Writer / io.Reader,省去了中间字节切片。在 http 包 的 Handler 里最常用。
处理嵌套 JSON
type Address struct {
City string `json:"city"`
Country string `json:"country"`
}
type Person struct {
Name string `json:"name"`
Address Address `json:"address"`
}
jsonData := []byte(`{"name":"Tom","address":{"city":"Beijing","country":"China"}}`)
var p Person
json.Unmarshal(jsonData, &p)
fmt.Println(p.Address.City) // Beijing嵌套结构体对应嵌套 JSON,结构关系自动匹配。
常见写法
| 写法 | 说明 |
|---|---|
json.Marshal(v) | Go 值转 JSON 字节切片 |
json.MarshalIndent(v, "", " ") | Go 值转格式化 JSON |
json.Unmarshal(data, &v) | JSON 字节切片转 Go 值 |
json.NewEncoder(w).Encode(v) | 直接写入 io.Writer |
json.NewDecoder(r).Decode(&v) | 直接从 io.Reader 读取 |
`json:"name"` | 指定 JSON 字段名 |
`json:",omitempty"` | 零值时省略 |
`json:"-"` | 忽略字段 |
特点
Marshal返回[]byte,不是string,打印时要string()转换Unmarshal第二个参数必须是指针- 结构体标签控制字段映射,是 Go 里最优雅的元数据方式
omitempty在 API 响应中很常用,避免返回一堆无意义的零值字段- JSON 数字默认解析为
float64,需要整数要手动类型断言 Encoder/Decoder配合io.Writer/io.Reader效率更高,省内存- 只有首字母大写的字段才会被 JSON 包处理
理解
encoding/json 的核心就两个操作:Marshal 把结构体变成 JSON,Unmarshal 把 JSON 变回结构体。结构体标签是灵魂——它告诉 JSON 包”这个字段叫什么名字、什么时候省略、要不要忽略”。在 HTTP 接口里配合 Encoder / Decoder 用,几行代码就能完成 JSON 数据的收发,非常顺手。
引出
JSON 里经常要处理时间字符串(如 "2026-07-20T15:30:00Z"),解析和格式化时间需要专门的时间处理。接下来看 Go time 包,学习 Go 中时间的创建、格式化和计算。