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



相关文章
|
4月前
|
安全 Go 数据处理
Go语言CSP编程实战:通道通信技术
Go语言CSP编程实战:通道通信技术
49 0
|
4月前
|
Go
高效Go语言编程:os包实用技术大揭示
高效Go语言编程:os包实用技术大揭示
46 0
|
2月前
|
SQL 前端开发 Go
编程笔记 GOLANG基础 001 为什么要学习Go语言
编程笔记 GOLANG基础 001 为什么要学习Go语言
|
13天前
|
数据库连接 Go 数据库
【Go 语言专栏】Go 语言中的错误注入与防御编程
【4月更文挑战第30天】本文探讨了Go语言中的错误注入和防御编程。错误注入是故意引入错误以测试系统异常情况下的稳定性和容错性,包括模拟网络故障、数据库错误和手动触发错误。防御编程则强调编写代码时考虑并预防错误,确保程序面对异常时稳定运行。Go语言的错误处理机制包括多返回值和自定义错误类型。结合错误注入和防御编程,可以提升软件质量和可靠性,打造更健壮的系统。开发人员应重视这两方面,以实现更优质的软件产品。
|
13天前
|
网络协议 安全 Go
【Go语言专栏】Go语言中的WebSocket编程
【4月更文挑战第30天】本文介绍了在Go语言中使用WebSocket进行实时Web应用开发的方法。通过第三方包`gorilla/websocket`,开发者可建立WebSocket服务器和客户端。文中展示了如何创建服务器,升级HTTP连接,以及处理读写消息的示例代码。同时,客户端的创建和通信过程也得以阐述。文章还提及WebSocket的生命周期管理、性能与安全性考虑,以及实践中的最佳做法。通过学习,读者将能运用Go语言构建高效、实时的Web应用。
|
13天前
|
算法 Java Go
【Go语言专栏】Go语言中的泛型编程探索
【4月更文挑战第30天】Go语言新引入的泛型编程支持使得代码更通用灵活。通过类型参数在函数和接口定义中实现泛型,如示例中的泛型函数`Swap`和泛型接口`Comparator`。泛型应用包括数据结构、算法实现、函数包装和错误处理,提升代码复用与维护性。这一特性扩展了Go语言在云计算、微服务、区块链等领域的应用潜力。
|
13天前
|
JSON 编解码 Go
【Go语言专栏】Go语言中的JSON编码与解码
【4月更文挑战第30天】Go语言内置JSON编码解码支持,简化了数据交换。`json.Marshal`用于将Go结构体转换为JSON,如示例中`Person`结构体的编码。`json.Unmarshal`则将JSON数据反序列化到结构体,需传入结构体变量的地址。错误处理至关重要,特别是在处理大量数据时,要注意性能优化,如避免不必要的转换和重复操作。了解自定义编码解码和最佳实践能提升开发效率。掌握这些技能,有助于构建高效Go应用。
|
14天前
|
JSON JavaScript 前端开发
Golang深入浅出之-Go语言JSON处理:编码与解码实战
【4月更文挑战第26天】本文探讨了Go语言中处理JSON的常见问题及解决策略。通过`json.Marshal`和`json.Unmarshal`进行编码和解码,同时指出结构体标签、时间处理、omitempty使用及数组/切片区别等易错点。建议正确使用结构体标签,自定义处理`time.Time`,明智选择omitempty,并理解数组与切片差异。文中提供基础示例及时间类型处理的实战代码,帮助读者掌握JSON操作。
23 1
Golang深入浅出之-Go语言JSON处理:编码与解码实战
|
18天前
|
SQL 关系型数据库 MySQL
Golang数据库编程详解 | 深入浅出Go语言原生数据库编程
Golang数据库编程详解 | 深入浅出Go语言原生数据库编程
|
19天前
|
网络协议 Linux Go
Go语言TCP Socket编程(下)
Go语言TCP Socket编程