编码与序列化
包总览
| 包 | 格式 | 用途 | 推荐度 |
|---|
encoding/json | JSON | API 通信、配置文件 | ★★★★★ |
encoding/xml | XML | SOAP、SVG、RSS | ★★★☆☆ |
encoding/csv | CSV | 数据导出、报表 | ★★★★☆ |
encoding/binary | 二进制 | 网络协议、文件格式 | ★★★☆☆ |
encoding/gob | Go 二进制 | Go 进程间通信 | ★★☆☆☆ |
encoding/base64 | Base64 | 编码传输、数据 URI | ★★★★☆ |
encoding/hex | 十六进制 | 哈希值、调试输出 | ★★★☆☆ |
encoding/pem | PEM | 证书、密钥 | ★★☆☆☆ |
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 key | json:"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.Number 或 UseNumber() |
| 时间格式不对 | 默认用 RFC3339 | 自定义 MarshalJSON |
| 大整数溢出 | float64 无法精确表示 | 用 json.Number 或 string tag |
| nil vs 空切片 | []int(nil) 序列化为 null | 用 make([]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" |
cdata | CDATA | xml:",cdata" |
omitempty | 零值省略 | xml:"title,omitempty" |
- | 忽略 | xml:"-" |
innerxml | 原始 XML | xml:",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 | 典型用途 |
|---|
| StdEncoding | A-Za-z0-9+/ | = | 标准 Base64 |
| URLEncoding | A-Za-z0-9-_ | = | URL 安全 |
| RawStdEncoding | A-Za-z0-9+/ | 无 | 紧凑传输 |
| RawURLEncoding | A-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{})
| 对比 | JSON | gob |
|---|
| 跨语言 | ✓ | ✗(Go only) |
| 体积 | 大 | 小(紧凑二进制) |
| 速度 | 中 | 快 |
| 可读性 | ✓ | ✗ |
| schema | 外部 | 自描述 |
| 典型用途 | API 通信 | Go 内部 RPC、缓存序列化 |
序列化方案对比
| 格式 | 体积 | 速度 | 可读性 | 跨语言 | 典型用途 |
|---|
| JSON | 大 | 中 | ✓ | ✓ | REST API、配置 |
| XML | 最大 | 慢 | ✓ | ✓ | SOAP、RSS |
| YAML | 中 | 慢 | ✓ | ✓ | 配置文件 |
| gob | 小 | 快 | ✗ | ✗ | Go 内部通信 |
| protobuf | 最小 | 最快 | ✗ | ✓ | gRPC、高性能 RPC |
| msgpack | 小 | 快 | ✗ | ✓ | 缓存、消息队列 |
| CSV | 中 | 快 | ✓ | ✓ | 数据导出 |