JSON 是一种轻量级的数据交换格式,常用作前后端数据交换,Go 在 encoding/json 包中提供了对 JSON 的支持。
在线支持JSON转换Go的网站 https://mholt.github.io/json-to-go/

序列化

序列化在计算机科学中通常有以下定义:

  • 对同步控制而言,表示强制在同一时间内进行单一访问。
  • 在数据储存与发送的部分是指将一个对象存储至一个存储介质,例如文件或是存储器缓冲等,或者透过网络发送数据时进行编码的过程,可以是字节或是JSON等格式。而字节的或JSON编码格式可以还原完全相等的对象。这程序被应用在不同应用程序之间发送对象,以及服务器将对象存储到文件或数据库。相反的过程又称为反序列化。

把 Go struct 序列化成 JSON 对象,Go 提供了 Marshal 方法,正如其含义所示表示编排序列化,函数签名如下:

  1. func Marshal(v interface{}) ([]byte, error)

举例来说,比如下面的 Go struct:

  1. type Message struct {
  2. Name string
  3. Body string
  4. Time int64
  5. }

使用 Marshal 序列化:

  1. m := Message{"Alice", "Hello", 1294706395881547000}
  2. b, err := json.Marshal(m)
  3. fmt.Println(b) //{"Name":"Alice","Body":"Hello","Time":1294706395881547000}

序列化类型

在 Go 中并不是所有的类型都能进行序列化:
JSON object key 只支持 string,如果要对一个 Go map 类型进行编码,map 必须是 map[string]T(T是 json 包中支持的任何类型)

  • Channel、复杂(complex)、函数(function) 等 type 无法进行序列化
  • 数据中如果存在循环引用,则不能进行序列化,因为序列化时会进行递归
  • 指针(Pointer) 序列化之后是其指向的值或者是 nil

    JSON类型对应关系

Go 类型 json类型
bool booleans
float64 numbers
string strings
nil null

反序列化

反序列化函数是 Unmarshal ,其函数签名如下:

  1. func Unmarshal(data []byte, v interface{}) error

如果要进行反序列化,我们首先需要创建一个可以接受序列化数据的 Go struct:

  1. var m Message
  2. err := json.Unmarshal(b, &m)

JSON 对象一般都是小写表示,Marshal 之后 JSON 对象的首字母依然是大写,如果序列化之后名称想要改变如何实现,答案就是 struct tags

Struct Tag

Struct tag 可以决定 Marshal 和 Unmarshal 函数如何序列化和反序列化数据。

指定 JSON filed name

JSON object 中的 name 一般都是小写,我们可以通过 struct tag 来实现:

  1. type MyStruct struct {
  2. SomeField string `json:"some_field"`
  3. }

SomeField 序列化之后会变成 some_field。

  1. package main
  2. import (
  3. "encoding/json"
  4. "fmt"
  5. )
  6. type StuGender int
  7. const (
  8. MALE = 1 << iota
  9. FEMALE
  10. )
  11. type student struct {
  12. Id int64 `json:"id"`
  13. Name string `json:"name"`
  14. Gender StuGender `json:"gender"`
  15. Exams []exam `json:"exams"`
  16. }
  17. type exam struct {
  18. Lesson string `json:"lesson"`
  19. Score int `json:"score"`
  20. }
  21. func main() {
  22. ming := student{Id: 101, Name: "小明", Gender: MALE, Exams: []exam{{Lesson: "Chinese", Score: 98}, {Lesson: "Math", Score: 100}}}
  23. data, _ := json.Marshal(ming)
  24. var s student
  25. if err := json.Unmarshal(data, &s); err == nil {
  26. fmt.Println(s)
  27. }
  28. }

结果输出

  1. {101 小明 1 [{Chinese 98} {Math 100}]}

指定 field 是 empty 时的行为

使用 omitempty 可以告诉 Marshal 函数如果 field 的值是对应类型的 zero-value,那么序列化之后的 JSON object 中不包含此 field:

  1. type MyStruct struct {
  2. SomeField string `json:"some_field,omitempty"`
  3. }
  4. m := MyStruct{}
  5. b, err := json.Marshal(m) //{}

如果 SomeField == “” ,序列化之后的对象就是 {}

跳过 field

Struct tag “-” 表示跳过指定的 filed:

  1. type MyStruct struct {
  2. SomeField string `json:"some_field"`
  3. Passwd string `json:"-"`
  4. }
  5. m := MyStruct{}
  6. b, err := json.Marshal(m) //{"some_feild":""}

即序列化的时候不输出,这样可以有效保护需要保护的字段不被序列化。

反序列化任意 JSON 数据

在序列化之前如果不知道 JSON 数据格式,我们使用 interface{} 来存储。encoding/json 包使用 map[string]interface{} 和 []interface{} 储存任意的 json 对象和数组;其可以被反序列化为任何的 json blob 存储到接口值中。直接使用 Unmarshal 把这个数据反序列化,并保存在map[string]interface{} 中,要访问这个数据,我们可以使用类型断言。反序列化任意json数据:

  1. package main
  2. import (
  3. "encoding/json"
  4. "fmt"
  5. )
  6. type StuGender int
  7. const (
  8. MALE = 1 << iota
  9. FEMALE
  10. )
  11. type student struct {
  12. Id int64 `json:"id"`
  13. Name string `json:"name"`
  14. Gender StuGender `json:"gender"`
  15. Exams []exam `json:"exams"`
  16. }
  17. type exam struct {
  18. Lesson string `json:"lesson"`
  19. Score int `json:"score"`
  20. }
  21. func main() {
  22. ming := student{Id: 101, Name: "小明", Gender: MALE, Exams: []exam{{Lesson: "Chinese", Score: 98}, {Lesson: "Math", Score: 100}}}
  23. data, _ := json.Marshal(ming)
  24. var m map[string]interface{}
  25. if err := json.Unmarshal(data, &m); err == nil {
  26. fmt.Println(m)
  27. }
  28. for k,v :=range m{
  29. switch vv := v.(type) {
  30. case string:
  31. fmt.Println(k, "是string", vv)
  32. case bool:
  33. fmt.Println(k, "是bool", vv)
  34. case float64:
  35. fmt.Println(k, "float64", vv)
  36. case nil:
  37. fmt.Println(k, "是nil", vv)
  38. case []interface{}:
  39. fmt.Println(k, "是array:")
  40. for i, u := range vv {
  41. fmt.Println(i, u)
  42. }
  43. default:
  44. fmt.Println(k, "未知数据类型")
  45. }
  46. }
  47. }

switch-type中,根据 json和Go数据类型对照表。比如Score是float64而不是int类型。

反序列化对 slice、map、pointer 的处理

我们定义一个 struct 继续对上面例子中的 b 进行反序列化:

  1. type FamilyMember struct {
  2. Name string
  3. Age int
  4. Parents []string
  5. }
  6. var m FamilyMember
  7. err := json.Unmarshal(b, &m)

这个例子是能够正常工作的,你一定也注意到了,struct 中包含一个 slice Parents ,slice 默认是 nil,之所以反序列化可以正常进行就是因为 Unmarshal 在序列化时进行了对 slice Parents 做了初始化,同理,对 map 和 pointer 都会做类似的工作,比如序列化如果 Pointer 不是 nil 首先进行 dereference 获得其指向的值,然后再进行序列化,反序列化时首先对 nil pointer 进行初始化

Stream JSON

除了 marshal 和 unmarshal 函数,Go 还提供了 Decoder 和 Encoder 对 stream JSON 进行处理,常见 request 中的 Body、文件等:

  1. jsonFile, err := os.Open("post.json")
  2. if err != nil {
  3. fmt.Println("Error opening json file:", err)
  4. return
  5. }
  6. defer jsonFile.Close()
  7. decoder := json.NewDecoder(jsonFile)
  8. for {
  9. var post Post
  10. err := decoder.Decode(&post)
  11. if err == io.EOF {
  12. break
  13. }
  14. if err != nil {
  15. fmt.Println("error decoding json:", err)
  16. return
  17. }
  18. fmt.Println(post)
  19. }
  1. // json.go
  2. package main
  3. import (
  4. "encoding/json"
  5. "fmt"
  6. "net/http"
  7. )
  8. type User struct {
  9. Firstname string `json:"firstname"`
  10. Lastname string `json:"lastname"`
  11. Age int `json:"age"`
  12. }
  13. func main() {
  14. http.HandleFunc("/decode", func(w http.ResponseWriter, r *http.Request) {
  15. var user User
  16. json.NewDecoder(r.Body).Decode(&user)
  17. fmt.Fprintf(w, "%s %s is %d years old!", user.Firstname, user.Lastname, user.Age)
  18. })
  19. http.HandleFunc("/encode", func(w http.ResponseWriter, r *http.Request) {
  20. peter := User{
  21. Firstname: "John",
  22. Lastname: "Doe",
  23. Age: 25,
  24. }
  25. json.NewEncoder(w).Encode(peter)
  26. })
  27. http.ListenAndServe(":8080", nil)
  28. }

分别请求api

  1. $ go run json.go
  2. $ curl -s -XPOST -d'{"firstname":"Elon","lastname":"Musk","age":48}' http://localhost:8080/decode
  3. Elon Musk is 48 years old!
  4. $ curl -s http://localhost:8080/encode
  5. {"firstname":"John","lastname":"Doe","age":25}

NewDecoder 和 NewEncoder 函数分别封装了 io.Reader 和 io.Writer 接口。

  1. func NewDecoder(r io.Reader) *Decoder
  2. func NewEncoder(w io.Writer) *Encoder

把 json 直接写入文件,可以使用 json.NewEncoder 初始化文件(或者任何实现 io.Writer 的类型),并调用 Encode();从文件中读取json可以使用 json.Decoder 和 Decode() 函数:

  1. package main
  2. import (
  3. "encoding/json"
  4. "fmt"
  5. "os"
  6. )
  7. type StuGender int
  8. const (
  9. MALE = 1 << iota
  10. FEMALE
  11. )
  12. type Student struct {
  13. Id int64 `json:"id"`
  14. Name string `json:"name"`
  15. Gender StuGender `json:"gender"`
  16. Exams []exam `json:"exams"`
  17. }
  18. type exam struct {
  19. Lesson string `json:"lesson"`
  20. Score int `json:"score"`
  21. }
  22. func main() {
  23. stu := Student{Id: 101, Name: "小明", Gender: MALE, Exams: []exam{{Lesson: "Chinese", Score: 98}, {Lesson: "Math", Score: 100}}}
  24. // 存储文件
  25. f, err := os.Create("./student.json")
  26. if err != nil {
  27. fmt.Println(err)
  28. }
  29. if err = json.NewEncoder(f).Encode(stu);err!=nil{
  30. fmt.Println(err)
  31. }
  32. // 读取文件
  33. file, err := os.Open("./student.json")
  34. if err != nil {
  35. fmt.Println(err)
  36. }
  37. defer file.Close()
  38. var s Student
  39. if err = json.NewDecoder(file).Decode(&s);err==nil{
  40. fmt.Println(s)
  41. }else {
  42. fmt.Println(err)
  43. }
  44. }

输出结果

  1. {101 小明 1 [{Chinese 98} {Math 100}]}

嵌入式 struct 的序列化

Go 支持对 nested struct 进行序列化和反序列化:

  1. type App struct {
  2. Id string `json:"id"`
  3. }
  4. type Org struct {
  5. Name string `json:"name"`
  6. }
  7. type AppWithOrg struct {
  8. App
  9. Org
  10. }
  11. func main() {
  12. data := []byte(`
  13. {
  14. "id": "k34rAT4",
  15. "name": "My Awesome Org"
  16. }
  17. `)
  18. var b AppWithOrg
  19. json.Unmarshal(data, &b)
  20. fmt.Printf("%#v", b)
  21. a := AppWithOrg{
  22. App: App{
  23. Id: "k34rAT4",
  24. },
  25. Org: Org{
  26. Name: "My Awesome Org",
  27. },
  28. }
  29. data, _ = json.Marshal(a)
  30. fmt.Println(string(data))
  31. }

Nested struct 虽然看起来有点怪异,有些时候它将非常有用。

自定义序列化函数

Go JSON package 中定了两个 Interface MarshalerUnmarshaler ,实现这两个 Interface 可以让你定义的 type 支持序列化操作。

  1. func NewDecoder(r io.Reader) *Decoder
  2. func NewEncoder(w io.Writer) *Encoder

参考

https://qvault.io/golang/json-golang/