go常用库:encoding/json

99 阅读5分钟

深入探索 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.Marshalerjson.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包的用法。

欢迎关注公众号"彼岸流天"。