强曰为道
与天地相似,故不违。知周乎万物,而道济天下,故不过。旁行而不流,乐天知命,故不忧.
文档目录

Go 语言完全指南 / 19 - 编码:JSON、XML、Base64、Gob、Protocol Buffers

19 - 编码

19.1 JSON

序列化

package main

import (
    "encoding/json"
    "fmt"
    "time"
)

type User struct {
    ID        int       `json:"id"`
    Name      string    `json:"name"`
    Email     string    `json:"email"`
    Password  string    `json:"-"`              // 忽略
    Age       int       `json:"age,omitempty"`   // 零值忽略
    Tags      []string  `json:"tags"`
    CreatedAt time.Time `json:"created_at"`
    Address   *Address  `json:"address,omitempty"` // 指针,nil 时忽略
}

type Address struct {
    City    string `json:"city"`
    Country string `json:"country"`
}

func main() {
    user := User{
        ID:        1,
        Name:      "Alice",
        Email:     "alice@example.com",
        Password:  "secret",
        Age:       30,
        Tags:      []string{"admin", "user"},
        CreatedAt: time.Now(),
        Address:   &Address{City: "Beijing", Country: "China"},
    }

    // 序列化
    data, err := json.Marshal(user)
    if err != nil {
        panic(err)
    }
    fmt.Println(string(data))

    // 美化输出
    data2, _ := json.MarshalIndent(user, "", "  ")
    fmt.Println(string(data2))
}

反序列化

func main() {
    jsonStr := `{
        "id": 1,
        "name": "Alice",
        "email": "alice@example.com",
        "age": 30,
        "tags": ["admin", "user"],
        "address": {
            "city": "Beijing",
            "country": "China"
        }
    }`

    var user User
    err := json.Unmarshal([]byte(jsonStr), &user)
    if err != nil {
        panic(err)
    }
    fmt.Printf("%+v\n", user)
}

JSON 流式处理

// 解码器(适合大文件或流式数据)
func decodeUsers(r io.Reader) ([]User, error) {
    var users []User
    decoder := json.NewDecoder(r)
    
    // 跳过数组开始
    decoder.Token() // [
    
    for decoder.More() {
        var user User
        if err := decoder.Decode(&user); err != nil {
            return nil, err
        }
        users = append(users, user)
    }
    
    return users, nil
}

// 编码器
func encodeUsers(w io.Writer, users []User) error {
    encoder := json.NewEncoder(w)
    encoder.SetIndent("", "  ")
    return encoder.Encode(users)
}

处理未知结构

func main() {
    jsonStr := `{"name": "Alice", "age": 30, "scores": [95, 87, 92]}`

    // 使用 map[string]any
    var data map[string]any
    json.Unmarshal([]byte(jsonStr), &data)
    fmt.Println(data["name"])   // Alice
    fmt.Println(data["age"])    // 30(float64 类型!)

    // 使用 any
    var v any
    json.Unmarshal([]byte(jsonStr), &v)

    // 使用 json.RawMessage 延迟解析
    type Message struct {
        Type    string          `json:"type"`
        Payload json.RawMessage `json:"payload"`
    }

    msg := Message{}
    json.Unmarshal([]byte(`{"type": "user", "payload": {"name": "Alice"}}`), &msg)

    switch msg.Type {
    case "user":
        var user User
        json.Unmarshal(msg.Payload, &user)
        fmt.Println(user.Name)
    }
}

19.2 XML

import "encoding/xml"

type Person struct {
    XMLName   xml.Name `xml:"person"`
    ID        int      `xml:"id,attr"`
    FirstName string   `xml:"name>first"`
    LastName  string   `xml:"name>last"`
    Age       int      `xml:"age"`
    Comments  []string `xml:"comments>comment"`
}

func main() {
    // 序列化
    p := Person{
        ID:        1,
        FirstName: "Alice",
        LastName:  "Wang",
        Age:       30,
        Comments:  []string{"Great!", "Awesome"},
    }
    data, _ := xml.MarshalIndent(p, "", "  ")
    fmt.Println(string(data))

    // 反序列化
    xmlStr := `<person id="1"><name><first>Alice</first><last>Wang</last></name><age>30</age></person>`
    var p2 Person
    xml.Unmarshal([]byte(xmlStr), &p2)
    fmt.Printf("%+v\n", p2)
}

19.3 Base64

import "encoding/base64"

func main() {
    data := []byte("Hello, 世界!")

    // 标准 Base64
    encoded := base64.StdEncoding.EncodeToString(data)
    fmt.Println(encoded)

    decoded, _ := base64.StdEncoding.DecodeString(encoded)
    fmt.Println(string(decoded))

    // URL 安全 Base64
    urlEncoded := base64.URLEncoding.EncodeToString(data)
    fmt.Println(urlEncoded)

    // Raw(无填充)
    raw := base64.RawStdEncoding.EncodeToString(data)
    fmt.Println(raw)

    // 用于图片
    imgData, _ := os.ReadFile("logo.png")
    base64Img := base64.StdEncoding.EncodeToString(imgData)
    dataURI := "data:image/png;base64," + base64Img
    _ = dataURI
}

19.4 Gob

Go 专用的二进制编码格式,适合 Go 程序之间的通信。

import (
    "bytes"
    "encoding/gob"
    "fmt"
)

type User struct {
    Name  string
    Age   int
    Email string
}

func main() {
    // 注册类型(用于接口类型的编码)
    gob.Register(User{})

    // 编码
    var buf bytes.Buffer
    encoder := gob.NewEncoder(&buf)
    
    user := User{Name: "Alice", Age: 30, Email: "alice@example.com"}
    if err := encoder.Encode(user); err != nil {
        panic(err)
    }
    fmt.Printf("编码后 %d 字节\n", buf.Len())

    // 解码
    decoder := gob.NewDecoder(&buf)
    var decoded User
    if err := decoder.Decode(&decoded); err != nil {
        panic(err)
    }
    fmt.Printf("解码: %+v\n", decoded)

    // 编码多个值
    var buf2 bytes.Buffer
    enc := gob.NewEncoder(&buf2)
    enc.Encode(42)
    enc.Encode("hello")
    enc.Encode([]int{1, 2, 3})

    dec := gob.NewDecoder(&buf2)
    var n int
    var s string
    var nums []int
    dec.Decode(&n)
    dec.Decode(&s)
    dec.Decode(&nums)
    fmt.Println(n, s, nums) // 42 hello [1 2 3]
}

19.5 Protocol Buffers

// user.proto
syntax = "proto3";
package demo;
option go_package = "./pb";

message User {
    int32 id = 1;
    string name = 2;
    string email = 3;
    repeated string tags = 4;
}

service UserService {
    rpc GetUser(GetUserRequest) returns (User);
}

message GetUserRequest {
    int32 id = 1;
}
# 生成代码
protoc --go_out=. --go-grpc_out=. user.proto
import (
    "google.golang.org/protobuf/proto"
    pb "myproject/pb"
)

func main() {
    // 创建消息
    user := &pb.User{
        Id:    1,
        Name:  "Alice",
        Email: "alice@example.com",
        Tags:  []string{"admin", "user"},
    }

    // 序列化
    data, err := proto.Marshal(user)
    if err != nil {
        panic(err)
    }
    fmt.Printf("Protobuf: %d bytes\n", len(data))

    // 反序列化
    user2 := &pb.User{}
    if err := proto.Unmarshal(data, user2); err != nil {
        panic(err)
    }
    fmt.Printf("User: %+v\n", user2)
}

格式对比

格式可读性体积速度跨语言
JSON✅ 高
XML✅ 高最大最慢
Gob❌ 二进制❌ Go 专用
Protobuf❌ 二进制
MessagePack❌ 二进制

🏢 业务场景

  1. API 开发:JSON 序列化 HTTP 响应
  2. 配置文件:JSON/XML/YAML 配置解析
  3. gRPC 服务:Protocol Buffers 定义接口
  4. 缓存存储:Gob 编码对象存储到 Redis
  5. 文件传输:Base64 编码二进制数据

📖 扩展阅读