Go 编程 | 连载 28 - Go 与 JSON

简介: Go 编程 | 连载 28 - Go 与 JSON

一、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:[]}



相关文章
|
12天前
|
数据采集 监控 Java
go语言编程学习
【11月更文挑战第3天】
30 7
|
17天前
|
Unix Linux Go
go进阶编程:Golang中的文件与文件夹操作指南
本文详细介绍了Golang中文件与文件夹的基本操作,包括读取、写入、创建、删除和遍历等。通过示例代码展示了如何使用`os`和`io/ioutil`包进行文件操作,并强调了错误处理、权限控制和路径问题的重要性。适合初学者和有经验的开发者参考。
|
25天前
|
Java 大数据 Go
Go语言:高效并发的编程新星
【10月更文挑战第21】Go语言:高效并发的编程新星
47 7
|
1月前
|
Go 数据处理 调度
Go语言中的并发模型:解锁高效并行编程的秘诀
本文将探讨Go语言中独特的并发模型及其在现代软件开发中的应用。通过深入分析 Goroutines 和 Channels,我们将揭示这一模型如何简化并行编程,提升应用性能,并改变开发者处理并发任务的方式。不同于传统多线程编程,Go的并发方法以其简洁性和高效性脱颖而出,为开发者提供了一种全新的编程范式。
|
1月前
|
JSON 数据格式 Python
Python编程:利用JSON模块编程验证用户
Python编程:利用JSON模块编程验证用户
|
1月前
|
并行计算 算法 搜索推荐
探索Go语言的高并发编程与性能优化
【10月更文挑战第10天】探索Go语言的高并发编程与性能优化
|
2月前
|
存储 缓存 Go
go语言编程系列(五)
go语言编程系列(五)
|
2月前
|
搜索推荐 Java 编译器
go语言编程系列(四)
go语言编程系列(四)
|
2月前
|
存储 JSON 安全
go语言编程系列(七)
go语言编程系列(七)
|
2月前
|
存储 安全 编译器
go语言编程系列(六)
go语言编程系列(六)