跳到主要内容

Go JSON 处理

JSON(JavaScript Object Notation)是一种轻量级的数据交换格式,在 Web 开发和 API 通信中被广泛使用。Go 语言的 encoding/json 包提供了完整的 JSON 编解码支持。

JSON 基础

JSON 数据类型

JSON 支持以下数据类型:

JSON 类型Go 类型
对象structmap[string]interface{}
数组slicearray
字符串string
数字intfloat64
布尔值bool
nullnil

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.Encoderjson.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, &reg)
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 处理的核心内容:

  1. 序列化json.Marshaljson.MarshalIndent
  2. 反序列化json.Unmarshal
  3. JSON 标签:字段名、omitempty、忽略字段
  4. 流式处理json.Encoderjson.Decoder
  5. 自定义序列化:实现 Marshaler/Unmarshaler 接口
  6. 延迟解析json.RawMessage
  7. 错误处理:语法错误、类型错误
  8. 性能优化:对象池、第三方库

练习

  1. 编写一个函数,将结构体切片保存为 JSON 文件
  2. 实现一个支持多种日期格式的自定义时间类型
  3. 使用 RawMessage 实现一个消息分发系统
  4. 对比标准库和 json-iterator 的性能差异