Go JSON 处理
JSON(JavaScript Object Notation)是一种轻量级的数据交换格式,在 Web 开发和 API 通信中被广泛使用。Go 语言的 encoding/json 包提供了完整的 JSON 编解码支持。
JSON 基础
JSON 数据类型
JSON 支持以下数据类型:
| JSON 类型 | Go 类型 |
|---|---|
| 对象 | struct 或 map[string]interface{} |
| 数组 | slice 或 array |
| 字符串 | string |
| 数字 | int、float64 等 |
| 布尔值 | bool |
| null | nil |
JSON 示例
{
"name": "张三",
"age": 25,
"email": "[email protected]",
"active": true,
"roles": ["admin", "user"],
"address": {
"city": "北京",
"street": "长安街"
}
}
序列化(Marshal)
基本序列化
将 Go 数据结构转换为 JSON 字节切片:
package main
import (
"encoding/json"
"fmt"
)
func main() {
user := map[string]interface{}{
"name": "张三",
"age": 25,
"email": "[email protected]",
}
data, err := json.Marshal(user)
if err != nil {
fmt.Println("序列化错误:", err)
return
}
fmt.Println(string(data))
}
结构体序列化
package main
import (
"encoding/json"
"fmt"
)
type User struct {
Name string `json:"name"`
Age int `json:"age"`
Email string `json:"email"`
}
func main() {
user := User{
Name: "张三",
Age: 25,
Email: "[email protected]",
}
data, _ := json.Marshal(user)
fmt.Println(string(data))
}
重要规则:
- 只有导出的字段(首字母大写)才会被序列化
- 使用
json:"fieldname"标签指定 JSON 字段名
格式化输出
使用 json.MarshalIndent 生成格式化的 JSON:
package main
import (
"encoding/json"
"fmt"
)
type User struct {
Name string `json:"name"`
Age int `json:"age"`
Email string `json:"email"`
}
func main() {
user := User{
Name: "张三",
Age: 25,
Email: "[email protected]",
}
data, _ := json.MarshalIndent(user, "", " ")
fmt.Println(string(data))
}
输出:
{
"name": "张三",
"age": 25,
"email": "[email protected]"
}
JSON 标签详解
type User struct {
Name string `json:"name"` // 指定字段名
Age int `json:"age"` // 指定字段名
Email string `json:"email,omitempty"` // 为空时不输出
Password string `json:"-"` // 忽略该字段
Address string `json:"address,omitempty"`
Phone string `json:"phone,string"` // 输出为字符串
}
标签选项:
| 选项 | 说明 |
|---|---|
json:"name" | 指定 JSON 字段名 |
json:"name,omitempty" | 字段为零值时忽略 |
json:"-" | 忽略该字段 |
json:",omitempty" | 使用原字段名,零值忽略 |
json:",string" | 数字输出为字符串 |
omitempty 示例
package main
import (
"encoding/json"
"fmt"
)
type User struct {
Name string `json:"name"`
Age int `json:"age,omitempty"`
Email string `json:"email,omitempty"`
Address string `json:"address,omitempty"`
}
func main() {
user1 := User{Name: "张三", Age: 25, Email: "[email protected]"}
data1, _ := json.Marshal(user1)
fmt.Println("完整数据:", string(data1))
user2 := User{Name: "李四"}
data2, _ := json.Marshal(user2)
fmt.Println("部分数据:", string(data2))
}
输出:
完整数据: {"name":"张三","age":25,"email":"[email protected]"}
部分数据: {"name":"李四"}
切片和 Map 序列化
package main
import (
"encoding/json"
"fmt"
)
func main() {
names := []string{"张三", "李四", "王五"}
data, _ := json.Marshal(names)
fmt.Println("切片:", string(data))
scores := map[string]int{
"张三": 90,
"李四": 85,
"王五": 92,
}
data, _ = json.Marshal(scores)
fmt.Println("Map:", string(data))
}
反序列化(Unmarshal)
基本反序列化
将 JSON 字节切片解析为 Go 数据结构:
package main
import (
"encoding/json"
"fmt"
)
type User struct {
Name string `json:"name"`
Age int `json:"age"`
Email string `json:"email"`
}
func main() {
jsonStr := `{"name":"张三","age":25,"email":"[email protected]"}`
var user User
err := json.Unmarshal([]byte(jsonStr), &user)
if err != nil {
fmt.Println("反序列化错误:", err)
return
}
fmt.Printf("Name: %s\n", user.Name)
fmt.Printf("Age: %d\n", user.Age)
fmt.Printf("Email: %s\n", user.Email)
}
重要:必须传入指针,否则无法修改原变量。
反序列化到 Map
当 JSON 结构不确定时,可以使用 map[string]interface{}:
package main
import (
"encoding/json"
"fmt"
)
func main() {
jsonStr := `{"name":"张三","age":25,"active":true}`
var result map[string]interface{}
json.Unmarshal([]byte(jsonStr), &result)
fmt.Println("name:", result["name"])
fmt.Println("age:", result["age"])
fmt.Println("active:", result["active"])
for key, value := range result {
fmt.Printf("%s: %v (%T)\n", key, value, value)
}
}
嵌套结构反序列化
package main
import (
"encoding/json"
"fmt"
)
type Address struct {
City string `json:"city"`
Street string `json:"street"`
}
type User struct {
Name string `json:"name"`
Age int `json:"age"`
Address Address `json:"address"`
}
func main() {
jsonStr := `{
"name": "张三",
"age": 25,
"address": {
"city": "北京",
"street": "长安街"
}
}`
var user User
json.Unmarshal([]byte(jsonStr), &user)
fmt.Printf("姓名: %s\n", user.Name)
fmt.Printf("城市: %s\n", user.Address.City)
fmt.Printf("街道: %s\n", user.Address.Street)
}
切片反序列化
package main
import (
"encoding/json"
"fmt"
)
type User struct {
Name string `json:"name"`
Age int `json:"age"`
}
func main() {
jsonStr := `[
{"name": "张三", "age": 25},
{"name": "李四", "age": 30},
{"name": "王五", "age": 28}
]`
var users []User
json.Unmarshal([]byte(jsonStr), &users)
for i, user := range users {
fmt.Printf("%d: %s (%d岁)\n", i+1, user.Name, user.Age)
}
}
流式处理
对于大文件或网络流,使用 json.Encoder 和 json.Decoder:
Encoder 写入
package main
import (
"encoding/json"
"os"
)
type User struct {
Name string `json:"name"`
Age int `json:"age"`
}
func main() {
users := []User{
{"张三", 25},
{"李四", 30},
{"王五", 28},
}
file, _ := os.Create("users.json")
defer file.Close()
encoder := json.NewEncoder(file)
encoder.SetIndent("", " ")
encoder.Encode(users)
}
Decoder 读取
package main
import (
"encoding/json"
"fmt"
"os"
)
type User struct {
Name string `json:"name"`
Age int `json:"age"`
}
func main() {
file, _ := os.Open("users.json")
defer file.Close()
var users []User
decoder := json.NewDecoder(file)
decoder.Decode(&users)
for _, user := range users {
fmt.Printf("%s: %d岁\n", user.Name, user.Age)
}
}
逐行读取大文件
package main
import (
"bufio"
"encoding/json"
"fmt"
"os"
)
type User struct {
Name string `json:"name"`
Age int `json:"age"`
}
func main() {
file, _ := os.Open("users.jsonl")
defer file.Close()
scanner := bufio.NewScanner(file)
for scanner.Scan() {
var user User
json.Unmarshal(scanner.Bytes(), &user)
fmt.Printf("%s: %d岁\n", user.Name, user.Age)
}
}
自定义序列化
实现 Marshaler 接口
package main
import (
"encoding/json"
"fmt"
"strings"
"time"
)
type CustomTime struct {
time.Time
}
func (ct CustomTime) MarshalJSON() ([]byte, error) {
formatted := fmt.Sprintf(`"%s"`, ct.Format("2006-01-02"))
return []byte(formatted), nil
}
func (ct *CustomTime) UnmarshalJSON(data []byte) error {
str := strings.Trim(string(data), `"`)
t, _ := time.Parse("2006-01-02", str)
ct.Time = t
return nil
}
type Event struct {
Name string `json:"name"`
StartTime CustomTime `json:"start_time"`
}
func main() {
event := Event{
Name: "会议",
StartTime: CustomTime{time.Now()},
}
data, _ := json.MarshalIndent(event, "", " ")
fmt.Println("序列化:", string(data))
var decoded Event
json.Unmarshal(data, &decoded)
fmt.Println("反序列化:", decoded.Name, decoded.StartTime.Format("2006-01-02"))
}
自定义类型转换
package main
import (
"encoding/json"
"fmt"
"strconv"
)
type BoolString bool
func (bs BoolString) MarshalJSON() ([]byte, error) {
if bs {
return json.Marshal("yes")
}
return json.Marshal("no")
}
func (bs *BoolString) UnmarshalJSON(data []byte) error {
var s string
json.Unmarshal(data, &s)
*bs = s == "yes"
return nil
}
type Config struct {
Enabled BoolString `json:"enabled"`
}
func main() {
config := Config{Enabled: true}
data, _ := json.Marshal(config)
fmt.Println(string(data))
var decoded Config
json.Unmarshal([]byte(`{"enabled":"yes"}`), &decoded)
fmt.Println("Enabled:", bool(decoded.Enabled))
}
延迟解析
使用 json.RawMessage
json.RawMessage 允许延迟解析 JSON 数据:
package main
import (
"encoding/json"
"fmt"
)
type Message struct {
Type string `json:"type"`
Data json.RawMessage `json:"data"`
}
type User struct {
Name string `json:"name"`
Age int `json:"age"`
}
type Product struct {
ID int `json:"id"`
Title string `json:"title"`
}
func main() {
jsonStr := `{
"type": "user",
"data": {"name": "张三", "age": 25}
}`
var msg Message
json.Unmarshal([]byte(jsonStr), &msg)
switch msg.Type {
case "user":
var user User
json.Unmarshal(msg.Data, &user)
fmt.Printf("用户: %s, %d岁\n", user.Name, user.Age)
case "product":
var product Product
json.Unmarshal(msg.Data, &product)
fmt.Printf("产品: %s\n", product.Title)
}
}
多态消息处理
package main
import (
"encoding/json"
"fmt"
)
type Request struct {
Type string `json:"type"`
Body json.RawMessage `json:"body"`
}
type LoginRequest struct {
Username string `json:"username"`
Password string `json:"password"`
}
type RegisterRequest struct {
Username string `json:"username"`
Password string `json:"password"`
Email string `json:"email"`
}
func handleRequest(data []byte) {
var req Request
json.Unmarshal(data, &req)
switch req.Type {
case "login":
var login LoginRequest
json.Unmarshal(req.Body, &login)
fmt.Printf("登录: %s\n", login.Username)
case "register":
var reg RegisterRequest
json.Unmarshal(req.Body, ®)
fmt.Printf("注册: %s, %s\n", reg.Username, reg.Email)
}
}
func main() {
loginJSON := `{
"type": "login",
"body": {"username": "admin", "password": "123456"}
}`
handleRequest([]byte(loginJSON))
registerJSON := `{
"type": "register",
"body": {"username": "user1", "password": "123456", "email": "[email protected]"}
}`
handleRequest([]byte(registerJSON))
}
错误处理
常见错误
package main
import (
"encoding/json"
"fmt"
)
func main() {
invalidJSON := `{"name": "张三", "age": }`
var result map[string]interface{}
err := json.Unmarshal([]byte(invalidJSON), &result)
if err != nil {
switch err := err.(type) {
case *json.SyntaxError:
fmt.Printf("语法错误: 位置 %d, %v\n", err.Offset, err)
case *json.UnmarshalTypeError:
fmt.Printf("类型错误: 字段 %s, 期望 %s, 实际 %s\n",
err.Field, err.Type, err.Value)
default:
fmt.Println("其他错误:", err)
}
}
}
严格模式
使用 DisallowUnknownFields 拒绝未知字段:
package main
import (
"encoding/json"
"fmt"
"strings"
)
type User struct {
Name string `json:"name"`
Age int `json:"age"`
}
func decodeStrict(data []byte, v interface{}) error {
decoder := json.NewDecoder(strings.NewReader(string(data)))
decoder.DisallowUnknownFields()
return decoder.Decode(v)
}
func main() {
jsonStr := `{"name": "张三", "age": 25, "extra": "未知字段"}`
var user User
err := decodeStrict([]byte(jsonStr), &user)
if err != nil {
fmt.Println("严格模式错误:", err)
}
}
性能优化
避免频繁序列化
package main
import (
"encoding/json"
"sync"
)
var userPool = sync.Pool{
New: func() interface{} {
return &User{}
},
}
type User struct {
Name string `json:"name"`
Age int `json:"age"`
}
func parseUser(data []byte) (*User, error) {
user := userPool.Get().(*User)
defer userPool.Put(user)
*user = User{}
err := json.Unmarshal(data, user)
return user, err
}
使用更快的 JSON 库
对于高性能场景,可以考虑第三方库:
json-iterator/go:兼容标准库,性能提升 2-3 倍bytedance/sonic:字节跳动开源,性能极高
import jsoniter "github.com/json-iterator/go"
var json = jsoniter.ConfigCompatibleWithStandardLibrary
func main() {
data, _ := json.Marshal(user)
json.Unmarshal(data, &user)
}
实战示例
配置文件读写
package main
import (
"encoding/json"
"fmt"
"os"
)
type Config struct {
Server struct {
Host string `json:"host"`
Port int `json:"port"`
} `json:"server"`
Database struct {
Host string `json:"host"`
Port int `json:"port"`
Name string `json:"name"`
User string `json:"user"`
Password string `json:"password"`
} `json:"database"`
Debug bool `json:"debug"`
}
func LoadConfig(path string) (*Config, error) {
file, err := os.Open(path)
if err != nil {
return nil, err
}
defer file.Close()
var config Config
decoder := json.NewDecoder(file)
if err := decoder.Decode(&config); err != nil {
return nil, err
}
return &config, nil
}
func SaveConfig(path string, config *Config) error {
file, err := os.Create(path)
if err != nil {
return err
}
defer file.Close()
encoder := json.NewEncoder(file)
encoder.SetIndent("", " ")
return encoder.Encode(config)
}
func main() {
config := &Config{}
config.Server.Host = "localhost"
config.Server.Port = 8080
config.Database.Host = "localhost"
config.Database.Port = 3306
config.Database.Name = "mydb"
config.Debug = true
SaveConfig("config.json", config)
loaded, _ := LoadConfig("config.json")
fmt.Printf("Server: %s:%d\n", loaded.Server.Host, loaded.Server.Port)
}
HTTP JSON API
package main
import (
"encoding/json"
"net/http"
)
type Response struct {
Code int `json:"code"`
Message string `json:"message"`
Data interface{} `json:"data,omitempty"`
}
func writeJSON(w http.ResponseWriter, code int, data interface{}) {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(code)
json.NewEncoder(w).Encode(Response{
Code: code,
Message: "success",
Data: data,
})
}
func writeError(w http.ResponseWriter, code int, message string) {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(code)
json.NewEncoder(w).Encode(Response{
Code: code,
Message: message,
})
}
小结
本章学习了 Go JSON 处理的核心内容:
- 序列化:
json.Marshal、json.MarshalIndent - 反序列化:
json.Unmarshal - JSON 标签:字段名、omitempty、忽略字段
- 流式处理:
json.Encoder、json.Decoder - 自定义序列化:实现 Marshaler/Unmarshaler 接口
- 延迟解析:
json.RawMessage - 错误处理:语法错误、类型错误
- 性能优化:对象池、第三方库
练习
- 编写一个函数,将结构体切片保存为 JSON 文件
- 实现一个支持多种日期格式的自定义时间类型
- 使用 RawMessage 实现一个消息分发系统
- 对比标准库和 json-iterator 的性能差异