4.8 序列化
JSON 编解码
基础用法
go
package main
import (
"encoding/json"
"fmt"
)
type Person struct {
Name string `json:"name"`
Age int `json:"age"`
City string `json:"city,omitempty"` // 空值时省略
}
func main() {
// 结构体转 JSON
p := Person{Name: "Alice", Age: 25, City: "Beijing"}
data, _ := json.Marshal(p)
fmt.Println(string(data)) // {"name":"Alice","age":25,"city":"Beijing"}
// 格式化输出
data2, _ := json.MarshalIndent(p, "", " ")
fmt.Println(string(data2))
/*
{
"name": "Alice",
"age": 25,
"city": "Beijing"
}
*/
// JSON 转结构体
var p2 Person
json.Unmarshal(data, &p2)
fmt.Println(p2.Name) // Alice
// JSON 转 map
var m map[string]interface{}
json.Unmarshal(data, &m)
fmt.Println(m["name"]) // Alice
}JSON 标签详解
go
package main
import (
"encoding/json"
"fmt"
)
type User struct {
ID int `json:"id"` // 字段名
Name string `json:"name"` // 字段名
Email string `json:"email,omitempty"` // 空值省略
Password string `json:"-"` // 忽略此字段
Token string `json:"token,omitempty"`
Role string `json:"role,string"` // 数字转字符串
}
func main() {
user := User{
ID: 1,
Name: "Alice",
Email: "",
Password: "secret",
Role: "admin",
}
data, _ := json.MarshalIndent(user, "", " ")
fmt.Println(string(data))
/*
{
"id": 1,
"name": "Alice",
"role": "admin"
}
*/
}自定义 Marshal/Unmarshal
go
package main
import (
"encoding/json"
"fmt"
"time"
)
// 自定义时间类型
type MyTime time.Time
func (mt MyTime) MarshalJSON() ([]byte, error) {
s := time.Time(mt).Format("2006-01-02 15:04:05")
return json.Marshal(s)
}
func (mt *MyTime) UnmarshalJSON(data []byte) error {
var s string
if err := json.Unmarshal(data, &s); err != nil {
return err
}
t, err := time.Parse("2006-01-02 15:04:05", s)
if err != nil {
return err
}
*mt = MyTime(t)
return nil
}
type Event struct {
Name string `json:"name"`
Time MyTime `json:"time"`
}
func main() {
event := Event{
Name: "会议",
Time: MyTime(time.Now()),
}
data, _ := json.MarshalIndent(event, "", " ")
fmt.Println(string(data))
// 解析
var event2 Event
json.Unmarshal(data, &event2)
fmt.Println(event2.Name)
}JSON 解码器高级用法
go
package main
import (
"encoding/json"
"fmt"
"strings"
)
func main() {
// 使用 Decoder 流式解码
jsonData := `{"name":"Alice"}{"name":"Bob"}{"name":"Charlie"}`
decoder := json.NewDecoder(strings.NewReader(jsonData))
for decoder.More() {
var m map[string]interface{}
decoder.Decode(&m)
fmt.Println(m["name"])
}
// UseNumber: 数字不转 float64,保留精度
jsonData2 := `{"price": 19.99, "count": 100}`
var m map[string]interface{}
decoder2 := json.NewDecoder(strings.NewReader(jsonData2))
decoder2.UseNumber()
decoder2.Decode(&m)
fmt.Println(m["price"]) // 19.99 (json.Number 类型)
fmt.Println(m["count"]) // 100 (json.Number 类型)
// DisallowUnknownFields: 不允许未知字段
type Person struct {
Name string `json:"name"`
}
jsonData3 := `{"name":"Alice","extra":"field"}`
var p Person
decoder3 := json.NewDecoder(strings.NewReader(jsonData3))
decoder3.DisallowUnknownFields()
if err := decoder3.Decode(&p); err != nil {
fmt.Println("错误:", err) // 会报错
}
}XML 编解码
基础用法
go
package main
import (
"encoding/xml"
"fmt"
)
type Person struct {
XMLName xml.Name `xml:"person"`
ID int `xml:"id,attr"` // 属性
Name string `xml:"name"` // 子元素
Age int `xml:"age"` // 子元素
City string `xml:"address>city"` // 嵌套元素
}
func main() {
p := Person{
ID: 1,
Name: "Alice",
Age: 25,
City: "Beijing",
}
// 结构体转 XML
data, _ := xml.MarshalIndent(p, "", " ")
fmt.Println(string(data))
/*
<person id="1">
<name>Alice</name>
<age>25</age>
<address>
<city>Beijing</city>
</address>
</person>
*/
// XML 转结构体
xmlData := `<person id="1"><name>Alice</name><age>25</age><address><city>Beijing</city></address></person>`
var p2 Person
xml.Unmarshal([]byte(xmlData), &p2)
fmt.Println(p2.Name)
}XML 标签详解
go
package main
import "encoding/xml"
type Product struct {
XMLName xml.Name `xml:"product"`
ID string `xml:"id,attr"` // 属性
Name string `xml:"name"` // 子元素
Price float64 `xml:"price"` // 子元素
Description string `xml:",chardata"` // 字符数据
Tags []string `xml:"tags>tag"` // 重复元素
Metadata Metadata `xml:"metadata"` // 嵌套
}
type Metadata struct {
Created string `xml:"created"`
Updated string `xml:"updated"`
}YAML 支持
使用 gopkg.in/yaml.v3
go
package main
import (
"fmt"
"gopkg.in/yaml.v3"
)
type Config struct {
Server ServerConfig `yaml:"server"`
Database DatabaseConfig `yaml:"database"`
Logging LoggingConfig `yaml:"logging"`
}
type ServerConfig struct {
Host string `yaml:"host"`
Port int `yaml:"port"`
}
type DatabaseConfig struct {
Driver string `yaml:"driver"`
Host string `yaml:"host"`
Port int `yaml:"port"`
Username string `yaml:"username"`
Password string `yaml:"password"`
}
type LoggingConfig struct {
Level string `yaml:"level"`
Format string `yaml:"format"`
}
func main() {
yamlData := `
server:
host: localhost
port: 8080
database:
driver: postgres
host: localhost
port: 5432
username: admin
password: secret
logging:
level: info
format: json
`
var config Config
yaml.Unmarshal([]byte(yamlData), &config)
fmt.Println(config.Server.Host)
// 转 YAML
data, _ := yaml.Marshal(&config)
fmt.Println(string(data))
}读取 YAML 配置文件
go
package main
import (
"os"
"gopkg.in/yaml.v3"
)
func LoadConfig(path string) (*Config, error) {
data, err := os.ReadFile(path)
if err != nil {
return nil, err
}
var config Config
if err := yaml.Unmarshal(data, &config); err != nil {
return nil, err
}
return &config, nil
}Protocol Buffers
定义 .proto 文件
protobuf
// person.proto
syntax = "proto3";
package main;
message Person {
int32 id = 1;
string name = 2;
string email = 3;
repeated string phones = 4;
}
message AddressBook {
repeated Person people = 1;
}生成 Go 代码
bash
# 安装 protoc 编译器
# 从 https://github.com/protocolbuffers/protobuf/releases 下载
# 安装 Go 插件
go install google.golang.org/protobuf/cmd/protoc-gen-go@latest
go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest
# 生成代码
protoc --go_out=. --go_opt=paths=source_relative person.proto使用生成的代码
go
package main
import (
"fmt"
"google.golang.org/protobuf/proto"
pb "path/to/your/proto"
)
func main() {
// 创建消息
person := &pb.Person{
Id: 1,
Name: "Alice",
Email: "alice@example.com",
Phones: []string{"123456", "789012"},
}
// 序列化
data, _ := proto.Marshal(person)
fmt.Println("序列化大小:", len(data))
// 反序列化
var person2 pb.Person
proto.Unmarshal(data, &person2)
fmt.Println("Name:", person2.Name)
}Gob 序列化
基础用法
go
package main
import (
"bytes"
"encoding/gob"
"fmt"
)
type Person struct {
Name string
Age int
}
func main() {
p := Person{Name: "Alice", Age: 25}
// 编码
var buf bytes.Buffer
encoder := gob.NewEncoder(&buf)
encoder.Encode(p)
fmt.Println("编码后大小:", buf.Len())
// 解码
var p2 Person
decoder := gob.NewDecoder(&buf)
decoder.Decode(&p2)
fmt.Println(p2.Name)
}Gob 传输
go
package main
import (
"encoding/gob"
"net"
)
// 服务端
func server() {
ln, _ := net.Listen("tcp", ":8080")
conn, _ := ln.Accept()
defer conn.Close()
encoder := gob.NewEncoder(conn)
encoder.Encode(Person{Name: "Alice", Age: 25})
}
// 客户端
func client() {
conn, _ := net.Dial("tcp", "localhost:8080")
defer conn.Close()
decoder := gob.NewDecoder(conn)
var p Person
decoder.Decode(&p)
}序列化性能对比
go
package main
import (
"bytes"
"encoding/gob"
"encoding/json"
"encoding/xml"
"testing"
"gopkg.in/yaml.v3"
)
type TestData struct {
ID int `json:"id" xml:"id" yaml:"id"`
Name string `json:"name" xml:"name" yaml:"name"`
Email string `json:"email" xml:"email" yaml:"email"`
Count int `json:"count" xml:"count" yaml:"count"`
}
func BenchmarkJSONMarshal(b *testing.B) {
data := TestData{ID: 1, Name: "Test", Email: "test@example.com", Count: 100}
for i := 0; i < b.N; i++ {
json.Marshal(data)
}
}
func BenchmarkJSONUnmarshal(b *testing.B) {
jsonStr := `{"id":1,"name":"Test","email":"test@example.com","count":100}`
for i := 0; i < b.N; i++ {
var data TestData
json.Unmarshal([]byte(jsonStr), &data)
}
}
func BenchmarkXMLMarshal(b *testing.B) {
data := TestData{ID: 1, Name: "Test", Email: "test@example.com", Count: 100}
for i := 0; i < b.N; i++ {
xml.Marshal(data)
}
}
func BenchmarkYAMLMarshal(b *testing.B) {
data := TestData{ID: 1, Name: "Test", Email: "test@example.com", Count: 100}
for i := 0; i < b.N; i++ {
yaml.Marshal(data)
}
}
func BenchmarkGobEncode(b *testing.B) {
data := TestData{ID: 1, Name: "Test", Email: "test@example.com", Count: 100}
for i := 0; i < b.N; i++ {
var buf bytes.Buffer
enc := gob.NewEncoder(&buf)
enc.Encode(data)
}
}
/*
典型性能结果:
- 速度:Gob > JSON > YAML > XML
- 大小:Gob < JSON < YAML < XML
- 可读性:YAML > JSON > XML > Gob
- 通用性:JSON > YAML > XML > Gob
使用建议:
- Web API: JSON (通用,跨语言)
- 配置文件:YAML (人类可读)
- 内部服务:Gob/Protobuf (高效,Go 专用)
- 文档交换:XML (标准,结构化)
*/序列化检查清单
[ ] JSON 字段使用合适的标签 (omitempty, -)
[ ] 敏感数据添加 json:"-" 标签
[ ] 时间类型使用自定义格式
[ ] 大数字使用 json.Number 保持精度
[ ] 解析未知字段时使用 DisallowUnknownFields
[ ] 配置文件使用 YAML
[ ] 高性能场景使用 Protobuf/Gob
[ ] 跨语言 API 使用 JSON
[ ] 验证输入数据大小避免 DoS
[ ] 添加适当的错误处理
[ ] 考虑版本兼容性