前置知识: Go

Go与JSON

00:00
4 min Beginner 2026/6/14

encoding/json详解

概述

JSON(JavaScript Object Notation)是最常用的数据交换格式,Web API、配置文件、消息队列等场景广泛使用。Go 标准库的 encoding/json 包提供了完整的 JSON 编码和解码功能,可以将 Go 结构体与 JSON 之间互相转换。

基础概念

在开始编码之前,需要理解 JSON 操作的几个核心概念:

  • 序列化(Marshal):将 Go 数据结构转换为 JSON 字符串。
  • 反序列化(Unmarshal):将 JSON 字符串转换为 Go 数据结构。
  • 结构体标签json:"name" 标签控制 JSON 字段名和行为。
  • 编码器(Encoder):流式写入 JSON,适合处理大量数据。
  • 解码器(Decoder):流式读取 JSON,适合从 HTTP 请求体等流中解析。

快速上手

最常用的 JSON 操作:

package main

import (
    "encoding/json"
    "fmt"
)

type User struct {
    Name  string `json:"name"`
    Age   int    `json:"age"`
    Email string `json:"email,omitempty"` // omitempty: 零值时省略
}

func main() {
    // 序列化:结构体 -> JSON
    user := User{Name: "小明", Age: 25}
    data, _ := json.Marshal(user)
    fmt.Println(string(data)) // {"name":"小明","age":25}

    // 反序列化:JSON -> 结构体
    jsonStr := `{"name":"小红","age":22,"email":"hong@example.com"}`
    var user2 User
    json.Unmarshal([]byte(jsonStr), &user2)
    fmt.Printf("%+v\n", user2) // {Name:小红 Age:22 Email:hong@example.com}
}

详细用法

1. 结构体标签

json 标签控制字段的 JSON 行为:

type Product struct {
    // 基本用法:指定 JSON 字段名
    Name string `json:"name"`

    // omitempty:零值(空字符串、0、nil)时省略该字段
    Description string `json:"description,omitempty"`

    // -:忽略该字段(不序列化也不反序列化)
    InternalID string `json:"-"`

    // ,-:序列化时忽略,但反序列化时可以填充
    TempField string `json:",omitempty"`

    // 不加标签:使用 Go 字段名(大写开头)
    Price float64

    // 字符串编码:将数字作为字符串输出
    ID int `json:"id,string"`
}

2. 基本类型序列化

// 切片
nums := []int{1, 2, 3}
data, _ := json.Marshal(nums) // [1,2,3]

// Map
m := map[string]int{"apple": 5, "banana": 3}
data, _ := json.Marshal(m) // {"apple":5,"banana":3}

// 嵌套结构
type Address struct {
    City    string `json:"city"`
    Country string `json:"country"`
}

type Person struct {
    Name    string  `json:"name"`
    Address Address `json:"address"`
}

person := Person{
    Name:    "小明",
    Address: Address{City: "北京", Country: "中国"},
}
data, _ := json.Marshal(person)
// {"name":"小明","address":{"city":"北京","country":"中国"}}

3. 美化输出

// 缩进格式输出
data, _ := json.MarshalIndent(user, "", "  ")
fmt.Println(string(data))
// {
//   "name": "小明",
//   "age": 25
// }

4. 处理未知结构

当 JSON 结构不确定时,使用 map[string]interface{}

jsonStr := `{"name":"小明","scores":{"math":95,"english":88}}`

var result map[string]interface{}
json.Unmarshal([]byte(jsonStr), &result)

name := result["name"].(string) // 类型断言
scores := result["scores"].(map[string]interface{})
math := scores["math"].(float64) // JSON 数字默认解析为 float64

fmt.Printf("姓名: %s, 数学: %.0f\n", name, math)

5. 流式编解码

处理 HTTP 请求/响应中的 JSON:

// 编码:将结构体写入 io.Writer
func handleAPI(w http.ResponseWriter, r *http.Request) {
    users := []User{{Name: "小明"}, {Name: "小红"}}

    w.Header().Set("Content-Type", "application/json")
    enc := json.NewEncoder(w)
    enc.Encode(users) // 直接写入 ResponseWriter
}

// 解码:从 io.Reader 读取 JSON
func handlePost(w http.ResponseWriter, r *http.Request) {
    var user User
    dec := json.NewDecoder(r.Body)
    err := dec.Decode(&user)
    if err != nil {
        http.Error(w, "无效的 JSON", http.StatusBadRequest)
        return
    }
    fmt.Printf("收到用户: %+v\n", user)
}

6. 自定义序列化

实现 json.Marshalerjson.Unmarshaler 接口:

type Time struct {
    time.Time
}

// 自定义序列化格式
func (t Time) MarshalJSON() ([]byte, error) {
    formatted := t.Format("2006-01-02 15:04:05")
    return json.Marshal(formatted)
}

// 自定义反序列化
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
}

7. 处理 JSON 数组

// 解析 JSON 数组到切片
jsonStr := `[{"name":"小明","age":25},{"name":"小红","age":22}]`

var users []User
json.Unmarshal([]byte(jsonStr), &users)

for _, u := range users {
    fmt.Printf("%s: %d\n", u.Name, u.Age)
}

8. 动态 JSON

处理结构不确定的 JSON 数据:

// 使用 json.RawMessage 延迟解析
type Event struct {
    Type string          `json:"type"`
    Data json.RawMessage `json:"data"` // 原始 JSON 字节
}

jsonStr := `{"type":"user_created","data":{"name":"小明"}}`

var event Event
json.Unmarshal([]byte(jsonStr), &event)

switch event.Type {
case "user_created":
    var user User
    json.Unmarshal(event.Data, &user)
case "order_created":
    var order Order
    json.Unmarshal(event.Data, &order)
}

常见场景

场景一:API 响应

// 统一 API 响应格式
type APIResponse 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(APIResponse{
        Code:    code,
        Message: "success",
        Data:    data,
    })
}

场景二:配置文件

type Config struct {
    Server   ServerConfig   `json:"server"`
    Database DatabaseConfig `json:"database"`
}

type ServerConfig struct {
    Port int    `json:"port"`
    Mode string `json:"mode"`
}

type DatabaseConfig struct {
    URL string `json:"url"`
}

func LoadConfig(path string) (*Config, error) {
    data, err := os.ReadFile(path)
    if err != nil {
        return nil, err
    }
    var cfg Config
    if err := json.Unmarshal(data, &cfg); err != nil {
        return nil, err
    }
    return &cfg, nil
}

场景三:嵌套 JSON 解析

type Order struct {
    ID     string  `json:"id"`
    Items  []Item  `json:"items"`
    Total  float64 `json:"total"`
}

type Item struct {
    Name  string  `json:"name"`
    Price float64 `json:"price"`
    Count int     `json:"count"`
}

jsonStr := `{
    "id": "ORD-001",
    "items": [
        {"name": "苹果", "price": 5.5, "count": 3},
        {"name": "香蕉", "price": 3.2, "count": 2}
    ],
    "total": 22.9
}`

var order Order
json.Unmarshal([]byte(jsonStr), &order)

注意事项与常见错误

  1. 字段必须导出:JSON 只能序列化导出的字段(大写字母开头)。小写开头的字段会被忽略。

  2. JSON 数字是 float64interface{} 解析 JSON时,类型float64,不是 int。需要类型断言转换

  3. 指针零值:如果需要区分”字段存在”和”字段零值”,使用指针类型:

type User struct {
    Age *int `json:"age,omitempty"` // nil 表示未设置,0 表示年龄为0
}
  1. Unmarshal 必须传指针json.Unmarshal(data, &user),必须传指针,否则无法修改

  2. 循环引用结构体之间不能有循环引用,否则序列化时会无限递归导致溢出。

  3. HTML 安全json.Marshal 默认会转义 <>&Unicode 转义序列。如果不需要转义,使用 json.NewEncoder设置 SetEscapeHTML(false)

  4. 时间格式time.Time 默认序列化为 RFC3339 式。如果需要其他格式,实现定义的 MarshalJSON。

进阶用法

第三方 JSON 库

标准库encoding/json 性能一般,性能场景可以使用:

  • jsoniter:兼容标准库性能提升 2-3 倍
  • ** sonic**:字节跳动开,基于 JIT性能
  • easyjson代码生成方式,零反射
// jsoniter 用法(与标准库兼容)
import jsoniter "github.com/json-iterator/go"

var json = jsoniter.ConfigCompatibleWithStandardLibrary

data, _ := json.Marshal(user)
json.Unmarshal(data, &user2)

json.Number

默认情况下 JSON解析为 float64,可能丢失精。使用 json.Number 保留原始数

dec := json.NewDecoder(bytes.NewReader(data))
dec.UseNumber() // 启用 Number 模式

var result map[string]interface{}
dec.Decode(&result)

// 获取数字字符串
num := result["id"].(json.Number).String()

知识检测

学习进度

-- 已学文档
--% 知识覆盖率

学习推荐

专注模式