讲解
JSON 是 Web 接口的事实标准数据格式,Go 标准库的 encoding/json 包提供了完整的编解码能力。序列化(Go 值 → JSON 文本)用 json.Marshal,反序列化(JSON 文本 → Go 值)用 json.Unmarshal,两个函数配合结构体就能覆盖绝大多数场景:Marshal 接收任何值返回 []byte,Unmarshal 接收 JSON 字节和目标变量的指针。
结构体字段与 JSON 键的对应规则:只有导出字段(大写开头)参与编解码;默认用字段名做键,实际项目里 JSON 习惯小写键,于是用「结构体标签」指定——字段后面跟反引号包起来的 tag:Name string json:"name"。标签里还能加选项:json:"name,omitempty" 表示零值时省略该字段,json:"-" 表示完全忽略。
JSON 结构不确定或只是临时取一两个字段时,可以反序列化到 map[string]any:所有 JSON 值被解析成对应的 Go 类型(对象→map、数组→[]any、数字→float64、字符串→string),取用时再类型断言。方便但放弃了类型安全,结构稳定的接口还是应该定义结构体——编译器会帮你发现字段写错。
示例
结构体序列化为 JSON:
package main
import (
"encoding/json"
"fmt"
)
type Book struct {
Title string `json:"title"`
Author string `json:"author"`
Price float64 `json:"price,omitempty"`
stock int // 小写字段不参与序列化
}
func main() {
b := Book{Title: "Go 入门", Author: "张三", Price: 59.9, stock: 100}
data, err := json.Marshal(b)
if err != nil {
fmt.Println("序列化失败:", err)
return
}
fmt.Println(string(data))
pretty, _ := json.MarshalIndent(b, "", " ")
fmt.Println(string(pretty))
}
JSON 反序列化到结构体,未知字段被忽略:
package main
import (
"encoding/json"
"fmt"
)
type Book struct {
Title string `json:"title"`
Price float64 `json:"price"`
}
func main() {
raw := `{"title":"Go 入门","price":59.9,"extra":"多余字段被忽略"}`
var b Book
if err := json.Unmarshal([]byte(raw), &b); err != nil {
fmt.Println("解析失败:", err)
return
}
fmt.Printf("%+v\n", b)
}
结构未知时用 map 接收:
package main
import (
"encoding/json"
"fmt"
)
func main() {
raw := `{"name":"小明","age":20,"tags":["go","json"]}`
var m map[string]any
if err := json.Unmarshal([]byte(raw), &m); err != nil {
fmt.Println("解析失败:", err)
return
}
fmt.Println("name:", m["name"])
fmt.Println("age(数字一律解析为 float64):", m["age"])
fmt.Println("tags:", m["tags"])
}
常见坑
- Unmarshal 忘记传指针:json.Unmarshal(data, b) 传值不生效(函数改不到调用方的变量),必须传 &b。好在这种错误会在运行时直接报 InvalidUnmarshalError,不难排查。
- 期待小写字段被序列化:只有导出字段参与 JSON 编解码,title string 小写字段会被静默跳过,序列化结果里「丢字段」先检查大小写。
- 数字精度的暗坑:JSON 数字默认解析为 float64,超大整数(如 18 位 ID)会丢精度。对精度敏感的 ID 用 json.Number 或 string 类型承接。
- time.Time 的格式错配:JSON 里的 "2025-01-01" 不是 time.Time 默认认的 RFC3339 格式,Unmarshal 会报错。要么用 RFC3339 格式传输,要么自定义 UnmarshalJSON。
- 在热路径反复 Marshal 大对象:encoding/json 基于反射,性能一般。极致性能场景可考虑预生成代码的第三方库(如 easyjson),但先把 profiling 做了再优化。
小结
Marshal/Unmarshal 配合结构体标签处理 JSON;导出字段才参与,omitempty 省略零值,map[string]any 应对未知结构。下一节用 JSON 知识搭一个真正的 HTTP 服务器。