深入探索 Go 语言的encoding/json:简化数据交换的艺术
在 Go 语言的广阔生态中,encoding/json包以其简洁而强大的功能脱颖而出,成为处理 JSON 数据的首选工具。它巧妙地连接了 Go 的数据结构与 JSON 字符串,让数据在两者之间自由穿梭,无论是构建 RESTful API、管理配置文件,还是实现跨系统数据交换,都显得游刃有余。现在,让我们一同揭开encoding/json的神秘面纱,深入探索其内在的魅力。
为何选择 JSON?
- 轻量高效:相较于 XML 等传统格式,JSON 以其更小的体积和更高的传输效率,成为现代互联网应用的宠儿。
- 易于阅读与维护:JSON 格式简洁明了,不仅人类易于阅读,也便于机器解析,大大降低了数据处理的复杂度。
- 广泛兼容性:几乎所有主流的编程语言都内置了对 JSON 的支持,这使得 JSON 成为了一种无界的语言,轻松实现跨平台、跨语言的数据交换。
Go 中的encoding/json实战
引入库与基础使用
在 Go 程序中,首先需要引入encoding/json包,以便使用其提供的函数。
import (
"encoding/json"
"fmt"
"log"
)
结构体到 JSON 的转换
将 Go 的结构体转换为 JSON 字符串,是encoding/json的基本功能之一。通过json.Marshal函数,我们可以轻松实现这一转换。
type Person struct {
Name string `json:"name"`
Age int `json:"age"`
Emails []string
Ignored string `json:"-"` // 忽略该字段
}
func main() {
p := Person{
Name: "John Doe",
Age: 30,
Emails: []string{"john@example.com", "doe@example.com"},
Ignored: "此字段将被忽略",
}
jsonData, err := json.Marshal(p)
if err != nil {
log.Fatalf("JSON 序列化失败: %s", err)
}
fmt.Println(string(jsonData))
// 输出: {"name":"John Doe","age":30,"Emails":["john@example.com","doe@example.com"]}
}
通过结构体标签,我们可以精细控制 JSON 字段的名称,甚至完全忽略某些字段。
JSON 到结构体的转换
同样地,json.Unmarshal函数允许我们将 JSON 字符串解析回 Go 的结构体,实现数据的反序列化。
func main() {
jsonStr := `{"name":"Jane Doe","age":28,"Emails":["jane@example.com","doe2@example.com"]}`
var p Person
err := json.Unmarshal([]byte(jsonStr), &p)
if err != nil {
log.Fatalf("JSON 反序列化失败: %s", err)
}
fmt.Printf("%+v\n", p)
// 输出: {Name:Jane Doe Age:28 Emails:[jane@example.com doe2@example.com] Ignored:}
}
注意,我们传递了&p(p 的地址)给Unmarshal函数,因为该函数需要修改传入的结构体变量。
进阶应用:自定义 Marshaler 与 Unmarshaler
对于某些特殊类型的数据,encoding/json还允许我们通过实现json.Marshaler和json.Unmarshaler接口来自定义其 JSON 的序列化和反序列化过程。
type CustomTime time.Time
// 实现 json.Marshaler 接口
func (ct CustomTime) MarshalJSON() ([]byte, error) {
// 自定义JSON序列化逻辑
return []byte(ct.Format(`"2006-01-02T15:04:05Z07:00"`)), nil
}
// 实现 json.Unmarshaler 接口
func (ct *CustomTime) UnmarshalJSON(data []byte) error {
// 自定义JSON反序列化逻辑
t, err := time.Parse(`"2006-01-02T15:04:05Z07:00"`, string(data))
if err != nil {
return err
}
*ct = CustomTime(t)
return nil
}
通过这种方式,我们可以为特定类型的数据量身定制其 JSON 表现形式,满足更加复杂和个性化的需求。
json 例子
1.序列化-匿名字段 (默认字段不冲突)
package main
import (
"encoding/json"
"fmt"
"testing"
)
// 学校
type School struct {
Name string `json:"schoolName"`
Address string `json:"schoolAddress"`
}
// 学生
type StudentA struct {
Name string `json:"name"`
// 匿名字段,而且没有json标签
School
}
// 序列化-匿名字段 (默认字段不冲突)
func TestAnonymousTagDifferent(t *testing.T) {
var student = StudentA{
Name: "小明",
School: School{
Name: "北京大学",
Address: "北京海淀区",
},
}
jsonByte, _ := json.Marshal(student)
fmt.Printf("json: %s \n", jsonByte)
}
/** 输出
=== RUN TestAnonymousTagSame
json: {"name":"小明","schoolName":"北京大学", "schoolAddress":"北京海淀区"}
--- PASS: TestAnonymousTagSame (0.00s)
PASS
*/
2.序列化-匿名字段 (字段标签冲突)
package main
import (
"encoding/json"
"fmt"
"testing"
)
// 学生
type StudentB struct {
// 标签名和班级名一样
Name string `json:"name"`
// 匿名字段,而且没有json标签
Class
}
// 班级
type Class struct {
// 标签名和学生名一样
Name string `json:"name"`
Desc string `json:"desc"`
}
// 序列化-匿名字段 (字段标签冲突)
func TestAnonymousTagSame(t *testing.T) {
var student = StudentB{
Name: "小明",
Class: Class{
Name: "高二(1)班",
Desc: "优秀班级",
},
}
jsonByte, _ := json.Marshal(student)
fmt.Printf("json: %s \n", jsonByte)
}
/** 输出
=== RUN TestAnonymousTagSame
json: {"name":"小明","desc":"优秀班级"}
--- PASS: TestAnonymousTagSame (0.00s)
PASS
*/
3.序列化-匿名字段,有 json 标签
package main
import (
"encoding/json"
"fmt"
"testing"
)
type Bird struct {
// json标签一样
Name string `json:"name"`
// 匿名字段,有json标签
Category `json:"category"`
}
type Category struct {
// json标签一样
Name string `json:"name"`
}
// 序列化-匿名字段,有json标签
func TestAnonymousWithTag(t *testing.T) {
b := Bird{
Name: "喜鹊",
Category: Category{Name: "鸟类"},
}
jsonByte, _ := json.Marshal(b)
fmt.Printf("json: %s \n", jsonByte)
}
/** 输出
=== RUN TestAnonymousWithTag
json: {"name":"喜鹊","category":{"name":"鸟类"}}
--- PASS: TestAnonymousWithTag (0.00s)
PASS
*/
4.序列化-匿名字段,有 json 标签
package main
import (
"encoding/json"
"fmt"
"testing"
)
// 序列化-匿名字段,有json标签
func TestAnonymousWithTagV4(t *testing.T) {
b := Bird{
Name: "喜鹊",
Category: Category{Name: "鸟类"},
}
jsonByte, _ := json.Marshal(b)
fmt.Printf("json: %s \n", jsonByte)
}
/** 输出
=== RUN TestAnonymousWithTag
json: {"name":"喜鹊","category":{"categoryName":"鸟类"}}
--- PASS: TestAnonymousWithTag (0.00s)
PASS
*/
5.容易错误的例子
package main
import (
"encoding/json"
"fmt"
)
// APIResponse 内部接口响应结构体
type APIResponse struct {
ErrCode int `json:"errCode"`
Data interface{} `json:"data,omitempty"`
ErrMsg string `json:"errMsg"`
}
type Data struct {
Name string `json:"name"`
Id int `json:"id"`
}
func main() {
a := `{"errCode":1,"errMsg":"success","data":{"name":"测试","id":1}}`
t := APIResponse{
Data: &Data{}, // 设置为&Data{} 会转成Data结构体,设置成Data{}会转成map[string]interface{}
}
err := json.Unmarshal([]byte(a), &t)
if err != nil {
fmt.Println(err)
}
}
总结
encoding/json包的用法。以其简洁、高效和灵活的特点,在 Go 语言的数据处理领域占据了举足轻重的地位。通过掌握这一包的基本用法和进阶技巧,我们能够更加轻松自如地处理 JSON 数据,为构建高效、可维护的 Go 语言应用奠定坚实的基础。
以上就是encoding/json包的用法。
欢迎关注公众号"彼岸流天"。