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



相关文章
|
7月前
|
设计模式 缓存 算法
Go如何进行高质量编程与性能调优实践
本文介绍了Go语言高质量编程与性能调优的实践方法。高质量编程包括良好的编码习惯(如清晰注释、命名规范)、代码风格与设计(如MVC模式)、简洁明了的代码原则,以及单元测试与代码重构的重要性。性能调优方面,涵盖算法优化、数据结构选择、I/O优化、内存管理、并行与并发处理优化及代码层面的改进。通过这些方法,可有效提升代码质量和系统性能。
152 13
|
7月前
|
分布式计算 Go C++
初探Go语言RPC编程手法
总的来说,Go语言的RPC编程是一种强大的工具,让分布式计算变得简单如同本地计算。如果你还没有试过,不妨挑战一下这个新的编程领域,你可能会发现新的世界。
177 10
|
7月前
|
JSON JavaScript 前端开发
Go语言JSON 序列化与反序列化 -《Go语言实战指南》
本文介绍了 Go 语言中使用 `encoding/json` 包实现 JSON 与数据结构之间的转换。内容涵盖序列化(`Marshal`)和反序列化(`Unmarshal`),包括基本示例、结构体字段标签的使用、控制字段行为的标签(如 `omitempty` 和 `-`)、处理 `map` 和切片、嵌套结构体序列化、反序列化未知结构(使用 `map[string]interface{}`)以及 JSON 数组的解析。最后通过表格总结了序列化与反序列化的方法及类型要求,帮助开发者快速掌握 JSON 数据处理技巧。
|
JSON JavaScript 前端开发
Go语言中json序列化的一个小坑,建议多留意一下
在Go语言开发中,JSON因其简洁和广泛的兼容性而常用于数据交换,但其在处理数字类型时存在精度问题。本文探讨了JSON序列化的一些局限性,并介绍了两种替代方案:Go特有的gob二进制协议,以及msgpack,两者都能有效解决类型保持和性能优化的问题。
364 7
|
JSON 前端开发 JavaScript
聊聊 Go 语言中的 JSON 序列化与 js 前端交互类型失真问题
在Web开发中,后端与前端的数据交换常使用JSON格式,但JavaScript的数字类型仅能安全处理-2^53到2^53间的整数,超出此范围会导致精度丢失。本文通过Go语言的`encoding/json`包,介绍如何通过将大整数以字符串形式序列化和反序列化,有效解决这一问题,确保前后端数据交换的准确性。
335 4
|
数据采集 监控 Java
go语言编程学习
【11月更文挑战第3天】
218 7
|
数据库连接 Go 数据库
Go语言中的错误注入与防御编程。错误注入通过模拟网络故障、数据库错误等,测试系统稳定性
本文探讨了Go语言中的错误注入与防御编程。错误注入通过模拟网络故障、数据库错误等,测试系统稳定性;防御编程则强调在编码时考虑各种错误情况,确保程序健壮性。文章详细介绍了这两种技术在Go语言中的实现方法及其重要性,旨在提升软件质量和可靠性。
197 1
|
Unix Linux Go
go进阶编程:Golang中的文件与文件夹操作指南
本文详细介绍了Golang中文件与文件夹的基本操作,包括读取、写入、创建、删除和遍历等。通过示例代码展示了如何使用`os`和`io/ioutil`包进行文件操作,并强调了错误处理、权限控制和路径问题的重要性。适合初学者和有经验的开发者参考。
219 4
|
Java 大数据 Go
Go语言:高效并发的编程新星
【10月更文挑战第21】Go语言:高效并发的编程新星
442 7
|
Go 数据处理 调度
Go语言中的并发模型:解锁高效并行编程的秘诀
本文将探讨Go语言中独特的并发模型及其在现代软件开发中的应用。通过深入分析 Goroutines 和 Channels,我们将揭示这一模型如何简化并行编程,提升应用性能,并改变开发者处理并发任务的方式。不同于传统多线程编程,Go的并发方法以其简洁性和高效性脱颖而出,为开发者提供了一种全新的编程范式。