理解 Go 语言中的 encoding/json
包
Go 语言通过 encoding/json
包提供了对 JSON 数据的强大支持,包括序列化、反序列化、自定义处理、数组处理、任意结构解析以及流式处理等。
1. 基本使用
1.1 结构体字段与 JSON 的映射
在 Go 中,结构体的字段可以通过 json
标签(JSON Tag)与 JSON 字段进行映射。如果不指定 json
标签,默认使用结构体字段名的蛇形命名(小写)作为 JSON 字段名。
go
type P struct { // 未使用json标签,自动根据字段名称进行绑定
Name string
Age int
Address string
Sex string
Time time.Time
}
type Person struct {
Name string `json:"name"`
Age int `json:"age"`
Address string `json:"-"`
Sex string `json:"sex,omitempty"`
}
- 结构体
P
:未定义json
标签,序列化时会使用结构体字段名作为 JSON 字段名。 - 结构体
Person
:Name
和Age
使用json
标签映射为name
和age
。Address
使用-
标签,表示不会被序列化。Sex
使用omitempty
,当字段为空时不会被序列化。
1.2 序列化与反序列化
go
func main() {
p1 := P{
Name: "Jon",
Age: 20,
Address: "beijing",
Sex: "男",
Time: time.Now(),
}
p2 := Person{
Name: "hon",
Age: 20,
Address: "beijing",
Sex: "",
}
// 编码(序列化)
json1, err := json.Marshal(p1)
// 处理错误
fmt.Println(string(json1))
// 输出示例: {"Name":"Jon","Age":20,"Address":"beijing","Sex":"男","Time":"..."}
json2, err := json.Marshal(p2)
// 处理错误
fmt.Println(string(json2))
// 输出示例: {"name":"hon","age":20}
// 解码(反序列化)
var p3 Person
err = json.Unmarshal(json1, &p3)
// 处理错误
fmt.Printf("%+v\n", p3)
// 输出: {Name:Jon Age:20 Address: Sex:男}
err = json.Unmarshal(json2, &p3)
// 处理错误
fmt.Printf("%+v\n", p3)
// 输出: {Name:hon Age:20 Address: Sex:}
}
json.Marshal
:将 Go 结构体序列化为 JSON 字符串。json.Unmarshal
:将 JSON 字符串反序列化为 Go 结构体。
2. 自定义 JSON 序列化与反序列化
有时候,默认的序列化和反序列化方式无法满足需求,这时可以通过实现 MarshalJSON
和 UnmarshalJSON
方法来自定义行为。
2.1 自定义类型示例
go
type Massachusetts struct {
Name string
}
type P3 struct {
Name string
Address *Massachusetts
}
// 自定义 MarshalJSON 方法
func (m *Massachusetts) MarshalJSON() ([]byte, error) {
return json.Marshal(struct {
State string `json:"state"`
}{
State: m.Name,
})
}
func Customize() {
address := Massachusetts{Name: "beijing"}
p := P3{
Name: "jon",
Address: &address,
}
// 编码
jsonBytes, err := json.Marshal(p)
// 处理错误
fmt.Println(string(jsonBytes))
// 输出: {"Name":"jon","Address":{"state":"beijing"}}
}
Massachusetts
结构体通过自定义MarshalJSON
方法,将Name
字段序列化为state
字段。
3. 处理 JSON 数组
Go 中的切片(Slice)和数组(Array)可以很方便地序列化为 JSON 数组,反向亦然。
3.1 JSON 数组示例
go
type P4 struct {
Name string `json:"name"`
Age int `json:"age"`
}
func JsonArray() {
people := []P4{
{Name: "p1", Age: 22},
{Name: "p2", Age: 23},
}
jonsBytes, err := json.Marshal(people)
// 处理错误
fmt.Println(string(jonsBytes))
// 输出: [{"name":"p1","age":22},{"name":"p2","age":23}]
}
JsonArray
函数展示了如何将切片序列化为 JSON 数组,以及如何进行反序列化。
4. 解析任意结构的 JSON 数据
在处理来自外部系统的 JSON 数据时,通常无法提前知道其具体结构。Go 提供了 map[string]interface{}
和 interface{}
来处理这种情况。
4.1 任意结构 JSON 示例
go
func JsonAny() {
jsonString := `{
"name":"p1",
"Age":21,
"email":"[email protected]"
}`
var m map[string]interface{}
err := json.Unmarshal([]byte(jsonString), &m)
// 处理错误
fmt.Printf("%+v\n", m)
// 输出: map[Age:21 email:[email protected] name:p1]
}
JsonAny
函数展示了如何将任意结构的 JSON 字符串解析为map[string]interface{}
,方便后续操作。
5. 流式处理 JSON 数据
对于大型 JSON 数据,逐行读写(流式处理)比一次性加载整个文件更加高效。Go 提供了 json.Decoder
和 json.Encoder
来处理流式 JSON 数据。
5.1 使用 json.Decoder
解码流式 JSON
go
func JsonNewDecoder() {
jsonData := `{"name":"John", "age":23}`
reader := strings.NewReader(jsonData)
decoder := json.NewDecoder(reader)
var p P4
if err := decoder.Decode(&p); err != nil {
fmt.Println(err)
}
fmt.Printf("%+v\n", p)
// 输出: {Name:John Age:23}
}
JsonNewDecoder
函数展示了如何使用json.Decoder
从io.Reader
中逐行读取和解析 JSON 数据。
5.2 使用 json.Encoder
编码流式 JSON
go
func JsonNewEncoder() {
p := P4{Name: "p1", Age: 22}
writer := &strings.Builder{}
encoder := json.NewEncoder(writer)
if err := encoder.Encode(&p); err != nil {
fmt.Println(err)
}
fmt.Println(writer.String())
// 输出: {"name":"p1","age":22}
}
JsonNewEncoder
函数展示了如何使用json.Encoder
将 Go 数据结构流式写入io.Writer
。
6. 时间类型的序列化与反序列化
在处理包含时间字段的 JSON 数据时,默认的序列化格式为 RFC3339。如果需要自定义时间格式,可以通过自定义类型实现。
6.1 自定义时间格式示例
go
type CustomTime time.Time
func (ct CustomTime) MarshalJSON() ([]byte, error) {
return []byte(fmt.Sprintf("\"%s\"", time.Time(ct).Format("2006-01-02 15:04:05"))), nil
}
func (ct *CustomTime) UnmarshalJSON(b []byte) error {
str := string(b)
str = str[1 : len(str)-1] // 去除双引号
t, err := time.Parse("2006-01-02 15:04:05", str)
if err != nil {
return err
}
*ct = CustomTime(t)
return nil
}
type P5 struct {
Name string `json:"name"`
CreatedAt CustomTime `json:"created_at"`
}
func CustomTimeExample() {
p := P5{
Name: "jon",
CreatedAt: CustomTime(time.Now()),
}
jsonBytes, err := json.Marshal(p)
// 处理错误
fmt.Println(string(jsonBytes))
// 输出: {"name":"jon","created_at":"2025-04-03 23:48:31"}
var p2 P5
err = json.Unmarshal(jsonBytes, &p2)
// 处理错误
fmt.Printf("%+v\n", p2)
// 输出: {Name:jon CreatedAt:2025-04-03 23:48:31 +0800 CST}
}
CustomTime
类型通过实现自定义的MarshalJSON
和UnmarshalJSON
方法,定义了时间的序列化和反序列化格式。
总结
Go 语言的 encoding/json
包提供了灵活而强大的工具来处理 JSON 数据。无论是基本的序列化和反序列化,还是复杂的自定义行为、数组处理、任意结构解析以及流式处理,encoding/json
都能很好地满足需求。理解并掌握这些功能,有助于在开发中高效地处理各种 JSON 数据相关的任务。