文章

编码与序列化

编码与序列化

包总览

格式用途推荐度
encoding/jsonJSONAPI 通信、配置文件★★★★★
encoding/xmlXMLSOAP、SVG、RSS★★★☆☆
encoding/csvCSV数据导出、报表★★★★☆
encoding/binary二进制网络协议、文件格式★★★☆☆
encoding/gobGo 二进制Go 进程间通信★★☆☆☆
encoding/base64Base64编码传输、数据 URI★★★★☆
encoding/hex十六进制哈希值、调试输出★★★☆☆
encoding/pemPEM证书、密钥★★☆☆☆

encoding/json

基本序列化/反序列化

type User struct {
    ID        int       `json:"id"`
    Name      string    `json:"name"`
    Email     string    `json:"email,omitempty"`
    Age       int       `json:"age"`
    Password  string    `json:"-"`                    // 不序列化
    CreatedAt time.Time `json:"created_at"`
    UpdatedAt *time.Time `json:"updated_at,omitempty"` // 指针:nil 时不输出
    Role      string    `json:"role,string"`           // 序列化为字符串
}

// Marshal:结构体 → JSON
user := User{ID: 1, Name: "小徐", Age: 28, Role: "admin"}
data, err := json.Marshal(user)
// {"id":1,"name":"小徐","email":"","age":28,"created_at":"...","role":"\"admin\""}

// MarshalIndent:带缩进
data, err := json.MarshalIndent(user, "", "  ")
// {
//   "id": 1,
//   "name": "小徐",
//   "age": 28
// }

// Unmarshal:JSON → 结构体
var u User
err := json.Unmarshal(data, &u)

struct tag 详解

tag 选项作用示例
字段名指定 JSON keyjson:"user_name"
omitempty零值时省略json:"name,omitempty"
-完全忽略json:"-"
string数字序列化为字符串json:"age,string"
omitempty + 指针nil 时省略json:"updated_at,omitempty"
// 特殊处理:用指针区分 "零值" 和 "未设置"
type UpdateUserReq struct {
    Name  *string `json:"name,omitempty"`   // nil=不更新, ""=更新为空
    Age   *int    `json:"age,omitempty"`    // nil=不更新, 0=更新为0
    Email *string `json:"email,omitempty"`
}

// PATCH 请求时非常有用
// {"name": "新名字"} → 只更新 name
// {"age": 0} → 更新 age 为 0(与不传 age 区分开)

流式编解码

// Encoder:写入到 io.Writer
f, _ := os.Create("users.json")
enc := json.NewEncoder(f)
enc.SetIndent("", "  ")
enc.SetEscapeHTML(false)  // 不转义 < > &
for _, user := range users {
    enc.Encode(user)  // 每次写入一个 JSON 对象 + 换行
}
// 生成 JSON Lines 格式(每行一个 JSON 对象)

// Decoder:从 io.Reader 读取
f, _ := os.Open("users.json")
dec := json.NewDecoder(f)
for {
    var u User
    if err := dec.Decode(&u); err != nil {
        if err == io.EOF {
            break
        }
        log.Fatal(err)
    }
    fmt.Println(u)
}

动态 JSON 处理

// 方式1:map[string]interface{}
var result map[string]interface{}
json.Unmarshal(data, &result)
// result["name"] → interface{} 需要类型断言

// 方式2:json.RawMessage(延迟解析)
type Response struct {
    Type    string          `json:"type"`
    Payload json.RawMessage `json:"payload"`  // 延迟解析
}
var resp Response
json.Unmarshal(data, &resp)

switch resp.Type {
case "user":
    var user User
    json.Unmarshal(resp.Payload, &user)
case "order":
    var order Order
    json.Unmarshal(resp.Payload, &order)
}

// 方式3:json.Decoder 使用 UseNumber(避免大数精度丢失)
dec := json.NewDecoder(bytes.NewReader(data))
dec.UseNumber()  // 数字解析为 json.Number 而非 float64
var v interface{}
dec.Decode(&v)
n, _ := v.(map[string]interface{})["id"].(json.Number).Int64()

自定义序列化

// 实现 json.Marshaler / json.Unmarshaler 接口

type Time time.Time

func (t Time) MarshalJSON() ([]byte, error) {
    return json.Marshal(time.Time(t).Unix())  // 序列化为时间戳
}

func (t *Time) UnmarshalJSON(data []byte) error {
    var ts int64
    if err := json.Unmarshal(data, &ts); err != nil {
        return err
    }
    *t = Time(time.Unix(ts, 0))
    return nil
}

// 自定义枚举序列化
type Status int

const (
    StatusPending Status = iota
    StatusActive
    StatusInactive
)

var statusNames = map[Status]string{
    StatusPending:  "pending",
    StatusActive:   "active",
    StatusInactive: "inactive",
}

func (s Status) MarshalJSON() ([]byte, error) {
    return json.Marshal(statusNames[s])
}

func (s *Status) UnmarshalJSON(data []byte) error {
    var name string
    if err := json.Unmarshal(data, &name); err != nil {
        return err
    }
    for k, v := range statusNames {
        if v == name {
            *s = k
            return nil
        }
    }
    return fmt.Errorf("unknown status: %s", name)
}

JSON 常见问题

问题原因解决方案
float64 精度丢失JSON 数字默认解析为 float64使用 json.NumberUseNumber()
时间格式不对默认用 RFC3339自定义 MarshalJSON
大整数溢出float64 无法精确表示json.Number 或 string tag
nil vs 空切片[]int(nil) 序列化为 nullmake([]int, 0) 初始化
HTML 字符被转义默认 SetEscapeHTML(true)enc.SetEscapeHTML(false)
// nil 切片 vs 空切片
type Data struct {
    Items []int `json:"items"`
}

json.Marshal(Data{})                    // {"items":null}
json.Marshal(Data{Items: []int{}})      // {"items":[]}
json.Marshal(Data{Items: []int(nil)})   // {"items":null}

// 修复:始终输出 []
type Data struct {
    Items []int `json:"items"`
}
func (d Data) MarshalJSON() ([]byte, error) {
    if d.Items == nil {
        d.Items = []int{}
    }
    type Alias Data
    return json.Marshal(Alias(d))
}

encoding/xml

type RSS struct {
    XMLName xml.Name `xml:"rss"`
    Version string   `xml:"version,attr"`
    Channel Channel  `xml:"channel"`
}

type Channel struct {
    Title       string `xml:"title"`
    Link        string `xml:"link"`
    Description string `xml:"description"`
    Items       []Item `xml:"item"`
}

type Item struct {
    Title       string `xml:"title"`
    Link        string `xml:"link"`
    Description string `xml:"description"`
    PubDate     string `xml:"pubDate"`
}

// 解析
var rss RSS
xml.Unmarshal(data, &rss)

// 生成
data, _ := xml.MarshalIndent(rss, "", "  ")

XML tag 选项

tag作用示例
字段名元素名xml:"title"
attr属性xml:"version,attr"
chardata字符数据xml:",chardata"
cdataCDATAxml:",cdata"
omitempty零值省略xml:"title,omitempty"
-忽略xml:"-"
innerxml原始 XMLxml:",innerxml"

encoding/csv

// 写入
f, _ := os.Create("users.csv")
defer f.Close()

w := csv.NewWriter(f)
defer w.Flush()  // 必须刷新

w.Write([]string{"ID", "Name", "Email"})  // 写表头
w.Write([]string{"1", "小徐", "xu@example.com"})
w.WriteAll([][]string{
    {"2", "小王", "wang@example.com"},
    {"3", "小李", "li@example.com"},
})

// 读取
f, _ := os.Open("users.csv")
defer f.Close()

r := csv.NewReader(f)
// 配置
r.Comma = ','            // 分隔符(默认逗号)
r.FieldsPerRecord = -1   // 允许每行列数不同
r.LazyQuotes = true      // 宽松引号处理

// 读取全部
records, err := r.ReadAll()

// 逐行读取(大文件推荐)
for {
    record, err := r.Read()
    if err == io.EOF {
        break
    }
    if err != nil {
        log.Fatal(err)
    }
    fmt.Println(record)
}

encoding/binary

import "encoding/binary"

// 字节序
// binary.LittleEndian:小端(x86/ARM,最常用)
// binary.BigEndian:大端(网络字节序)

// 写入
buf := make([]byte, 4)
binary.BigEndian.PutUint32(buf, 12345)  // 大端写入 4 字节

// 读取
val := binary.BigEndian.Uint32(buf)     // 12345

// 写入到 io.Writer
var w bytes.Buffer
binary.Write(&w, binary.LittleEndian, uint16(42))
binary.Write(&w, binary.LittleEndian, int32(-100))
binary.Write(&w, binary.LittleEndian, float64(3.14))

// 从 io.Reader 读取
var num uint16
binary.Read(reader, binary.LittleEndian, &num)

网络协议示例

// 自定义二进制协议
// | Magic (1B) | Version (1B) | Type (2B) | Length (4B) | Payload (NB) |

type Header struct {
    Magic   uint8
    Version uint8
    Type    uint16
    Length  uint32
}

func WritePacket(w io.Writer, header Header, payload []byte) error {
    buf := make([]byte, 8+len(payload))
    buf[0] = header.Magic
    buf[1] = header.Version
    binary.BigEndian.PutUint16(buf[2:4], header.Type)
    binary.BigEndian.PutUint32(buf[4:8], uint32(len(payload)))
    copy(buf[8:], payload)

    _, err := w.Write(buf)
    return err
}

func ReadPacket(r io.Reader) (Header, []byte, error) {
    headerBuf := make([]byte, 8)
    if _, err := io.ReadFull(r, headerBuf); err != nil {
        return Header{}, nil, err
    }

    header := Header{
        Magic:   headerBuf[0],
        Version: headerBuf[1],
        Type:    binary.BigEndian.Uint16(headerBuf[2:4]),
        Length:  binary.BigEndian.Uint32(headerBuf[4:8]),
    }

    payload := make([]byte, header.Length)
    if _, err := io.ReadFull(r, payload); err != nil {
        return header, nil, err
    }

    return header, payload, nil
}

encoding/base64

import "encoding/base64"

// 标准编码(带 padding,用 + /)
encoded := base64.StdEncoding.EncodeToString([]byte("hello"))
// "aGVsbG8="

decoded, _ := base64.StdEncoding.DecodeString(encoded)
// []byte("hello")

// URL 安全编码(用 - _ 替代 + /)
encoded := base64.URLEncoding.EncodeToString([]byte("hello"))
// "aGVsbG8="

// 无 padding 编码(JWT 常用)
encoded := base64.RawURLEncoding.EncodeToString([]byte("hello"))
// "aGVsbG8"

// 流式编码
enc := base64.NewEncoder(base64.StdEncoding, w)
enc.Write([]byte("hello"))
enc.Close()

dec := base64.NewDecoder(base64.StdEncoding, r)
io.Copy(os.Stdout, dec)
编码方式字符集padding典型用途
StdEncodingA-Za-z0-9+/=标准 Base64
URLEncodingA-Za-z0-9-_=URL 安全
RawStdEncodingA-Za-z0-9+/紧凑传输
RawURLEncodingA-Za-z0-9-_JWT

encoding/gob

import "encoding/gob"

// Go 专有的二进制序列化格式
// 比 JSON 更快更小,但只能 Go 之间使用

type User struct {
    ID   int
    Name string
    Tags map[string]string
}

// 编码
var buf bytes.Buffer
enc := gob.NewEncoder(&buf)
enc.Encode(User{ID: 1, Name: "小徐", Tags: map[string]string{"role": "admin"}})
// buf.Bytes() 是二进制数据

// 解码
dec := gob.NewDecoder(&buf)
var u User
dec.Decode(&u)

// 注册接口类型(需要序列化接口值时)
gob.Register(&User{})
gob.Register(&Admin{})
对比JSONgob
跨语言✗(Go only)
体积小(紧凑二进制)
速度
可读性
schema外部自描述
典型用途API 通信Go 内部 RPC、缓存序列化

序列化方案对比

格式体积速度可读性跨语言典型用途
JSONREST API、配置
XML最大SOAP、RSS
YAML配置文件
gobGo 内部通信
protobuf最小最快gRPC、高性能 RPC
msgpack缓存、消息队列
CSV数据导出