Go语言JSON序列化与反序列化

JSON(JavaScript Object Notation)是一种轻量级的数据交换格式,广泛应用于前后端通信和配置文件。Go语言通过标准库encoding/json提供了强大的JSON序列化(编码)和反序列化(解码)支持。通过结构体标签(struct tags),可以灵活控制JSON字段名、空值处理等行为。

核心功能:json.Marshal 将Go对象转为JSON;json.Unmarshal 将JSON转为Go对象。支持自定义时间格式、私有字段控制等。

1. 基本序列化与反序列化

定义结构体,并使用json.Marshal和json.Unmarshal进行转换。

示例:序列化与反序列化
package main

import (
    "encoding/json"
    "fmt"
)

type Person struct {
    Name string
    Age  int
    Email string
}

func main() {
    // 序列化 Go对象 -> JSON
    p := Person{Name: "Alice", Age: 30, Email: "alice@example.com"}
    jsonData, err := json.Marshal(p)
    if err != nil {
        fmt.Println("序列化失败:", err)
        return
    }
    fmt.Println(string(jsonData)) // {"Name":"Alice","Age":30,"Email":"alice@example.com"}

    // 反序列化 JSON -> Go对象
    var p2 Person
    err = json.Unmarshal(jsonData, &p2)
    if err != nil {
        fmt.Println("反序列化失败:", err)
        return
    }
    fmt.Printf("%+v\n", p2) // {Name:Alice Age:30 Email:alice@example.com}
}

2. 结构体标签(控制JSON字段)

通过反引号标签可以指定JSON字段名、是否忽略空值、字段忽略等。

标签用法
type User struct {
    ID        int    `json:"id"`                  // 重命名为 "id"
    Name      string `json:"name"`                // 重命名为 "name"
    Password  string `json:"-"`                   // 忽略此字段(不序列化)
    Email     string `json:"email,omitempty"`     // 空值时省略
    CreatedAt string `json:"created_at,omitempty"`
}

func main() {
    u := User{ID: 1, Name: "Bob", Password: "secret", Email: ""}
    data, _ := json.Marshal(u)
    fmt.Println(string(data)) // {"id":1,"name":"Bob"}
}
常用标签选项:omitempty(零值省略)、string(将数字转换为字符串)、-,omitempty(忽略)。

3. 格式化输出(MarshalIndent)

type Config struct {
    Host string `json:"host"`
    Port int    `json:"port"`
}

cfg := Config{Host: "localhost", Port: 8080}
prettyJSON, _ := json.MarshalIndent(cfg, "", "  ")
fmt.Println(string(prettyJSON))
// {
//   "host": "localhost",
//   "port": 8080
// }

4. 动态数据:解析到 map

当JSON结构不确定时,可以解析为map[string]interface{}或interface{}。

jsonStr := `{"name":"Alice","age":30,"hobbies":["reading","coding"]}`
var result map[string]interface{}
err := json.Unmarshal([]byte(jsonStr), &result)
if err != nil {
    panic(err)
}
fmt.Println(result["name"])     // Alice
fmt.Println(result["hobbies"])  // [reading coding]

// 类型断言获取具体值
hobbies := result["hobbies"].([]interface{})
fmt.Println(hobbies[0])         // reading
解析到interface{}时,数字默认是float64,注意类型断言。

5. 时间类型的序列化

标准库默认将time.Time转换为RFC3339格式字符串。可以通过自定义类型实现自定义格式。

自定义时间格式
type CustomTime struct {
    time.Time
}

func (ct CustomTime) MarshalJSON() ([]byte, error) {
    return []byte(`"` + ct.Format("2006-01-02") + `"`), nil
}

func (ct *CustomTime) UnmarshalJSON(data []byte) error {
    s := strings.Trim(string(data), `"`)
    t, err := time.Parse("2006-01-02", s)
    if err != nil {
        return err
    }
    ct.Time = t
    return nil
}

type Event struct {
    Name string     `json:"name"`
    Date CustomTime `json:"date"`
}

func main() {
    e := Event{Name: "Go Conference", Date: CustomTime{time.Now()}}
    data, _ := json.Marshal(e)
    fmt.Println(string(data)) // {"name":"Go Conference","date":"2025-06-08"}
}

6. 反序列化时禁止未知字段

默认情况下,如果JSON包含结构体没有的字段,Unmarshal会忽略。使用DisallowUnknownFields可以强制报错。

decoder := json.NewDecoder(strings.NewReader(`{"name":"Alice","age":30,"extra":true}`))
decoder.DisallowUnknownFields()
var u User
err := decoder.Decode(&u)
if err != nil {
    fmt.Println(err) // 报错:json: unknown field "extra"
}

7. 处理大数字:UseNumber

解析到interface{}时,大整数可能被解析为float64丢失精度。使用UseNumber保留原始数字字符串。

var data interface{}
decoder := json.NewDecoder(strings.NewReader(`{"id": 12345678901234567890}`))
decoder.UseNumber()
decoder.Decode(&data)
m := data.(map[string]interface{})
id := m["id"].(json.Number)
fmt.Println(id.String()) // 12345678901234567890

8. 流式编解码(Decoder/Encoder)

对于大文件或网络流,可以使用json.Decoder和json.Encoder逐条处理。

// 写入流
file, _ := os.Create("data.json")
enc := json.NewEncoder(file)
for _, item := range items {
    enc.Encode(item)
}

// 读取流
dec := json.NewDecoder(file)
var item Item
for dec.More() {
    dec.Decode(&item)
    // 处理 item
}

综合示例:处理REST API响应

模拟请求与解析
package main

import (
    "encoding/json"
    "fmt"
)

type Response struct {
    Code    int    `json:"code"`
    Message string `json:"message"`
    Data    User   `json:"data"`
}

type User struct {
    ID   int    `json:"id"`
    Name string `json:"name"`
    Role string `json:"role,omitempty"`
}

func main() {
    jsonResp := `{
        "code": 200,
        "message": "success",
        "data": {
            "id": 1001,
            "name": "John Doe"
        }
    }`

    var resp Response
    if err := json.Unmarshal([]byte(jsonResp), &resp); err != nil {
        fmt.Println("解析失败:", err)
        return
    }

    fmt.Printf("状态码: %d\n", resp.Code)
    fmt.Printf("用户: %s (ID=%d)\n", resp.Data.Name, resp.Data.ID)
}
状态码: 200
用户: John Doe (ID=1001)
小结:Go的encoding/json包简洁而强大,通过结构体标签可以精细控制JSON的生成与解析。掌握上述技巧后,可以高效处理API对接、配置文件读写等常见任务。下一章我们将学习Go语言中的数据库操作(database/sql)。