encoding/json - JSON 数据编解码
encoding/json 包提供了 JSON 数据的编解码功能,是 Go 中最常用的标准库之一。掌握 JSON 处理是开发 Web API、配置文件解析等场景的基础。
📌 核心概念
📤
Marshal
Go 结构体 → JSON
json.Marshal
📥
Unmarshal
JSON → Go 结构体
json.Unmarshal
🏷️
Struct Tag
字段映射和选项
`json:"name"`
🔧
自定义编解码
实现 json.Marshaler
MarshalJSON()
基础编解码
📝 Marshal 和 Unmarshal
package main
import (
"encoding/json"
"fmt"
)
type Person struct {
Name string `json:"name"`
Age int `json:"age"`
City string `json:"city,omitempty"`
}
func main() {
// 编码:结构体 → JSON
p := Person{Name: "Alice", Age: 30}
data, err := json.Marshal(p)
if err != nil {
panic(err)
}
fmt.Println(string(data))
// {"name":"Alice","age":30}
// 美化输出
pretty, _ := json.MarshalIndent(p, "", " ")
fmt.Println(string(pretty))
// 解码:JSON → 结构体
jsonStr := `{"name":"Bob","age":25,"city":"Beijing"}`
var p2 Person
err = json.Unmarshal([]byte(jsonStr), &p2)
if err != nil {
panic(err)
}
fmt.Printf("%+v\n", p2)
// {Name:Bob Age:25 City:Beijing}
}
💡 JSON 包要点
- 字段导出: 只有大写开头的字段才会被编解码
- omitempty: 零值时省略该字段
- -:
json:"-"忽略该字段 - 指针解码: Unmarshal 需要传递指针
- 错误处理: 始终检查返回的 error
Struct Tag 详解
📝 JSON Tag 选项
package main
import (
"encoding/json"
"fmt"
)
type User struct {
ID int `json:"id"` // 自定义字段名
Name string `json:"name"` // 正常映射
Email string `json:"email,omitempty"` // 空值时省略
Password string `json:"-"` // 忽略此字段
Role string `json:"role,omitempty"`
}
func main() {
// omitempty 测试
u1 := User{ID: 1, Name: "Alice"}
data, _ := json.Marshal(u1)
fmt.Println(string(data))
// {"id":1,"name":"Alice"}
u2 := User{ID: 2, Name: "Bob", Email: "bob@example.com", Role: "admin"}
data, _ = json.Marshal(u2)
fmt.Println(string(data))
// {"id":2,"name":"Bob","email":"bob@example.com","role":"admin"}
// 嵌套结构体
type Post struct {
ID int `json:"id"`
Title string `json:"title"`
Author User `json:"author"`
}
post := Post{
ID: 1,
Title: "Hello World",
Author: u2,
}
data, _ = json.Marshal(post)
fmt.Println(string(data))
}
解码到 interface{}
📝 处理未知结构的 JSON
package main
import (
"encoding/json"
"fmt"
)
func main() {
// 解码到 interface{} - 类型映射:
// JSON bool → bool
// JSON number → float64
// JSON string → string
// JSON array → []interface{}
// JSON object → map[string]interface{}
// JSON null → nil
jsonStr := `{
"name": "Alice",
"age": 30,
"skills": ["Go", "Python"],
"active": true,
"address": {
"city": "Beijing",
"zip": "100000"
}
}`
var data interface{}
json.Unmarshal([]byte(jsonStr), &data)
m := data.(map[string]interface{})
// 类型断言访问
name := m["name"].(string)
age := m["age"].(float64) // 注意:是 float64!
active := m["active"].(bool)
fmt.Printf("Name: %s, Age: %.0f, Active: %v\n", name, age, active)
// 访问数组
skills := m["skills"].([]interface{})
for _, skill := range skills {
fmt.Println(skill.(string))
}
// 访问嵌套对象
address := m["address"].(map[string]interface{})
fmt.Println(address["city"])
}
⚠️ interface{} 解码注意
- 数字类型: JSON 数字解码为
float64,不是int - 类型断言: 必须使用类型断言访问值
- 类型检查: 使用 type switch 安全访问
- 推荐: 优先使用结构体,类型更安全
自定义编解码
实现 Marshaler/Unmarshaler
📝 自定义 JSON 格式
package main
import (
"encoding/json"
"fmt"
"time"
)
// 自定义时间格式
type Time time.Time
func (t Time) MarshalJSON() ([]byte, error) {
s := time.Time(t).Format("2006-01-02 15:04:05")
return json.Marshal(s)
}
func (t *Time) UnmarshalJSON(data []byte) error {
var s string
if err := json.Unmarshal(data, &s); err != nil {
return err
}
parsed, err := time.Parse("2006-01-02 15:04:05", s)
if err != nil {
return err
}
*t = Time(parsed)
return nil
}
// 自定义类型:脱敏处理
type Password string
func (p Password) MarshalJSON() ([]byte, error) {
return json.Marshal("***") // 脱敏输出
}
type Account struct {
Username string `json:"username"`
Password Password `json:"password"`
Created Time `json:"created"`
}
func main() {
acc := Account{
Username: "alice",
Password: "secret123",
Created: Time(time.Now()),
}
data, _ := json.MarshalIndent(acc, "", " ")
fmt.Println(string(data))
// 密码会被脱敏
}
流式处理
📝 Encoder 和 Decoder
package main
import (
"encoding/json"
"os"
"strings"
)
func main() {
// Encoder: 流式编码
enc := json.NewEncoder(os.Stdout)
enc.Encode(map[string]int{"a": 1})
enc.Encode(map[string]int{"b": 2})
// Decoder: 流式解码
jsonData := `{"name": "Alice"} {"name": "Bob"}`
dec := json.NewDecoder(strings.NewReader(jsonData))
for {
var v map[string]string
if err := dec.Decode(&v); err != nil {
break
}
println(v["name"])
}
}
最佳实践
✅ JSON 使用建议
- 使用结构体: 类型安全,代码清晰
- 定义 Tag: 明确字段映射关系
- 错误处理: 始终检查 error
- 验证输入: Unmarshal 后验证数据
- 使用 omitempty: 减少不必要的数据传输
- 性能敏感: 考虑使用 sonic 等第三方库
🚨 常见陷阱
- 字段未导出: 小写字段不会被编解码
- 数字类型: interface{} 解码时数字是 float64
- 时间格式: 默认 RFC3339 格式,可能需要自定义
- 大数精度: float64 可能丢失 int64 精度
- 空 vs nil: 空切片和 nil 切片编码不同
总结
✅ 核心要点
- Marshal/Unmarshal: 基础编解码函数
- Struct Tag: 字段映射和选项
- omitempty: 零值时省略字段
- 自定义编解码: 实现 Marshaler 接口
- 流式处理: Encoder/Decoder 处理大数据
- 类型安全: 优先使用结构体而非 interface{}