JSON 是一种流行的数据交换格式,在Go中经常需要将结构体实例序列化为JSON字符串保存和传输。本文介绍使用Go中的
encoding/json
包将结构体序列化为JSON数据。
目录
- JSON序列化概述
- 定义可序列化的结构体
- 使用json.Marshal序列化
- 定制序列化逻辑
- 使用json.Unmarshal反序列化
- 处理空值和默认值
- 嵌套对象和匿名字段
- 序列化interface{}
- stream编码器
- JSON与XML对比
- 文本格式的优点
- 使用规范
- 序列化性能优化
JSON序列化概述
JSON是一种非常流行的结构化数据格式,被广泛用于网络传输和数据存储中。Go主要通过json包来处理JSON数据,有两个核心的函数:
json.Marshal
序列化Go对象为JOSN格式字节流json.Unmarshal
JSON数据解析为Go对象
定义可序列化的结构体
想要将一个结构体实例序列化为JSON,需要确保结构体字段可被json包访问,通常有两种方式:
- 字段首字母答谢,可以被外部包访问
- 通过tag指定字段名
比如:
type User struct {
ID int `json:"id"`
Name string `json:"name"`
Age int
}
使用序列化
package main
import (
"encoding/json"
"fmt"
)
type User struct {
Name string `json:"name"`
Age int `json:"age"`
}
func main() {
user := User{"John", 20}
// 序列化
buf, err := json.Marshal(user)
if err != nil {
fmt.Println(err)
return
}
// 打印JSON
fmt.Println(string(buf))
}
输出:
{"name":"John","age":20}
定制序列化逻辑
若需要控制序列化的细节,可以实现Marshaler接口,这样可以灵活控制各字段编码过程。
package main
import (
"encoding/json"
"fmt"
)
type User struct {
Name string `json:"name"`
Age int `json:"age"`
}
// 实现Marshaler接口
func (u User) MarshalJSON() ([]byte, error) {
return []byte(fmt.Sprintf(`{"name":"%s"}`, u.Name)), nil
}
func main() {
u := User{"John", 20}
jsonBytes, _ := json.Marshal(u)
fmt.Println(string(jsonBytes)) // {"name":"John"}
}
使用反序列化
package main
import (
"encoding/json"
"fmt"
)
type User struct {
Name string `json:"name"`
Age int `json:"age"`
}
func main() {
jsonStr := `{"name":"John","age":20}`
// 反序列化到user
var user User
err := json.Unmarshal([]byte(jsonStr), &user)
if err != nil {
fmt.Println(err)
return
}
fmt.Println(user.Name) // John
fmt.Println(user.Age) // 20
}
传入JSON数据以及对象指针,即可解析。
处理空值和默认值
对于空值的序列化,默认会忽略空值字段:
type User struct {
Name string
Age int
}
u := User{"John", 0}
jsonBytes, _ := json.Marshal(u)
fmt.Println(string(jsonBytes)) // {"Name":"John"}
可以通过定义 MarshalJSON 方法自定义处理空值的编码。
反序列化时,需要处理不存在的字段,可以使用指针或默认值。
嵌套对象和匿名字段
对于嵌套的对象字段和内嵌匿名类型,也可以正常序列化和反序列化:
type Info struct {
Addr string
}
type User struct {
Info // 嵌套Info
Name string
}
u := User{Info{"Beijing"}, "Tom"}
jsonBytes, _ := json.Marshal(u)
fmt.Println(string(jsonBytes)) // {"Addr":"Beijing","Name":"Tom"}
编码后嵌套的类型将被内联到结果中。
序列化空接口
json包可以处理任意的 interface{} 值:
data := map[string]interface{}{
"name": "John",
"age": 20,
}
jsonBytes, _ := json.Marshal(data)
fmt.Println(string(jsonBytes))
但编码结果会包含类型信息和Base64编码后的值,这比定制序列化更低效。
stream编码器
也可以使用streaming模式的encoder来进行序列化:
package main
import (
"encoding/json"
"os"
)
func main() {
type User struct {
Name string
Age int
}
user := User{"John", 20}
// 使用stream encoder
enc := json.NewEncoder(os.Stdout)
enc.Encode(user)
}
这种编码方式很灵活,可以实现各种自定义输出格式。
JSON与XML对比
JSON对比XML优点:
- 读取和解析更加方便快捷
- 存储占用更小且具有可读性
- 支持更多语言与平台
- 性能和解析速度更快
文本格式的优点
JSON与其他二进制序列化格式相比,文本格式JSON优点:
- 可读性强,便于打印调试
- 可复用现有文本处理工具
- 不依赖语言和平台
- 可支持部分更新修改
使用规范
- 格式化打印 JSON 数据
- 为类型和字段定义 Canonical 名称
- 提供完整的文档
- 版本控制 JSON 接口
- 采用更灵活的 schema 描述
序列化性能优化
- 提前指定好对象大小
- 重用分配的内存
- 尽量避免解析嵌套数据
- 按需完成自定义编码