携手创造,一起成长!这是我参加「日新方案 8 月更文应战」的第23天,点击检查活动概况

一、JSON 与 序列化和反序列化

在 Go 编程 | 连载 17 – 结构体方法 中层简单的介绍过 JSON 序列化与反序列化,在本文中将更具体的叙述 JSON 以及结构体标签是怎么细致的操控 JSON 的,以及怎么运用 HTTP 获取 API 的 JSON Response Body。

JSON 既 JavaScript Object Notation,JavaScript 目标表明法是一种用于存储和交流数据的格局,JSON 能够以键值对的方法表明数据,也能够经过数组的方法博鳌是数据。

JSON 开始是 JavaScript 的一个子集,现在 JSON 现已独立在言语之外,而且大多数言语都支撑 JSON 编码和解码,事实上 JSON 格局的数据现已成为规范而且现已替代 XML 既可扩展标记性言语,尤其是 Web 开发中前后端交互都是经过 JSON 格局数据实现的。

比如创造者中心页面的 API /content_api/v1/author_center/data/card 的响应数据为 JSON 格局

{
	"err_no": 0,
	"err_msg": "success",
	"data": {
		"date": "2022-08-31",
		"datas": {
			"all_article_collect": {
				"cnt": 269,
				"than_before": 6
			},
			"all_article_comment": {
				"cnt": 95,
				"than_before": 5
			},
			"all_article_digg": {
				"cnt": 6780,
				"than_before": 15
			},
			"all_article_display": {
				"cnt": 2338241,
				"than_before": 115639
			},
			"all_article_view": {
				"cnt": 150116,
				"than_before": 3734
			},
			"all_follower": {
				"cnt": 208,
				"than_before": 6
			}
		}
	}
}

json Tag 与序列化

Go 言语的规范库 encoding\json 提供了结构体编码和解码(序列化和反序列化)的函数 Marshal 和 Unmarshal 函数。

package main
import (
   "encoding/json"
   "fmt"
   "log"
)
func main(){
   adds := []string{"NYC", "BOS", "CHA", "WDC"}
   tony := Human{"Tony", 33, adds}
   // %+v 占位符会输出结构体时会带上 K,%v 输出结构体只有 V 没有 K
   fmt.Printf("%+v\n", tony)
   fmt.Printf("tony 变量的数据类型是:%T\n", tony)
   // 序列化
   tonyByte, err := json.Marshal(tony)
   if err != nil {
      // 报错会终止程序
      log.Fatal(err)
   } else {
      tonyJson := string(tonyByte)
      fmt.Println(tonyJson)
      fmt.Printf("tonyJson 数据的格局为:%T\n", tonyJson)
   }
}
type Human struct {
   Name string
   Age int
   Address []string
}

执行上述代码,输出成果如下:

{Name:Tony Age:33 Address:[NYC BOS CHA WDC]}
tony 变量的数据类型是:main.Human
{"Name":"Tony","Age":33,"Address":["NYC","BOS","CHA","WDC"]}
tonyJson 变量的数据类型是:string

依据输出成果能够确定现已成功的将一个结构体数据转换成字符串类型的 JSON 格局的数据,可是还有一个问题,在获取的 JSON 格局的数据中所有的 Key 的首字母都是大写的。

尽管 JSON 没有官方规范说 Key 的首字母一定要小写,可是在实际运用中的习气都是将 Key 的首字母小写,Key 有多个单词能够运用下划线或许驼峰命名法来命名。

对于这个问题能够运用结构体的 JSON 标签来解决,修正结构体为

type Human struct {
   Name string `json:"name"`
   Age int `json:"age"`
   Address []string `json:"address"`
}

保持 main 函数不变再次执行,输出成果如下:

{Name:Tony Age:33 Address:[NYC BOS CHA WDC]}
tony 变量的数据类型是:main.Human
{"name":"Tony","age":33,"address":["NYC","BOS","CHA","WDC"]}
tonyJson 变量的数据类型是:string

能够看出 Key 的首字母现已变为 json 标签指定的内容,除此之外 json 标签还能够指定为 omitempty ,该标签值表明当结构体字段为空时就疏忽该字段。

type Human struct {
   // 留意 json 标签中的内容运用逗号离隔,逗号后面不要有空格,否则会失效
   Name string `json:"name,omitempty"`
   Age int `json:"age,omitempty"`
   Address []string `json:"address,omitempty"`
}

修正 main 函数,将 adds 列表置空

adds := []string{}

再次执行 main 函数,输出成果如下:

{Name:Tony Age:33 Address:[]}
tony 变量的数据类型是:main.Human
{"name":"Tony","age":33}
tonyJson 变量的数据类型是:string

能够看出在添加了 omitempty 后,空列表在序列化时被疏忽,不再显示在 JSON 格局的数据中。

当结构体中的某些字段不想被序列化时,能够运用 json:"-",在序列化时不论内容是否为空,都会疏忽

type Human struct {
   Name string `json:"name,omitempty"`
   Age int `json:"age,omitempty"`
   Address []string `json:"address,omitempty"`
   Password string `json:"-"`
}

main 函数中从头实例化一个结构体

tony := Human{"Tony", 33, adds, "12138"}

再次执行 main 函数,输出成果如下:

{Name:Tony Age:33 Address:[NYC BOS] Password:12138}
tony 变量的数据类型是:main.Human
{"name":"Tony","age":33,"address":["NYC","BOS"]}
tonyJson 变量的数据类型是:string

序列化后的 JSON 数据中没有 Password 字段。

反序列化

JSON 反序列化也非常长江,在 Server 端可能收到来自 API、数据库或许配置文件中的 JSON 格局数据。

在 Go 中能够表明为字符串,encoding/json 规范库中的函数 Unmarshal 能够接收一个字节切片以及值,这个值就是 JSON 格局要封装的结构体的实例,因为结构体是值类型数据,所以这里一定要传递一个结构体指针。

func main(){
   // 反序列化
   tonyJson := "{"name":"Tony","age":33,"address":["NYC","BOS"]}"
   tonyByte := []byte(tonyJson)
   h := Human{}
   json.Unmarshal(tonyByte, &h)
   fmt.Printf("%+v\n", h)
   fmt.Printf("%T\n", h)
}
type Human struct {
   Name string `json:"name,omitempty"`
   Age int `json:"age,omitempty"`
   Address []string `json:"address,omitempty"`
}

执行上述代码,输出成果如下:

{Name:Tony Age:33 Address:[NYC BOS]}
main.Human

留意如果在反序列化时 Umarshal 函数的第二个参数是一个结构体而不是结构体指针,那么输出的成果将是一个空的结构体实例。

{Name: Age:0 Address:[]}