golang之JSON处理
在强类型语言中,JSON 通常很难处理 —— JSON 类型有字符串、数字、字典和数组。如果你使用的语言是 javascript、python、ruby 或 PHP,那么 JSON 有一个很大的好处就是在解析和编码数据时你不需要考虑类型。
// in PHP
$object = json_decode('{"foo":"bar"}');
// in javascript
const object = JSON.parse('{"foo":"bar"}')
在强类型语言中,你需要自己去定义怎么处理 JSON 对象的字符串、数字、字典和数组。在 Go 语言中,你使用内建的 API 时需要考虑如何更好地把一个 JSON 文件转换成 Go 的数据结构。我不打算深入研究在 Go 中如何处理 JSON 这个复杂的话题,我只列出两个代码的例子来阐述下这个问题。
解析/序列化为 map[string]interface
首先,来看这个程序
package main
import (
"encoding/json"
"fmt"
)
func main() {
byt := []byte(`{
"num":6.13,
"strs":["a","b"],
"obj":{"foo":{"bar":"zip","zap":6}}
}`)
var dat map[string]interface{}
if err := json.Unmarshal(byt, &dat); err != nil {
panic(err)
}
fmt.Println(dat)
num := dat["num"].(float64)
fmt.Println(num)
strs := dat["strs"].([]interface{})
str1 := strs[0].(string)
fmt.Println(str1)
obj := dat["obj"].(map[string]interface{})
obj2 := obj["foo"].(map[string]interface{})
fmt.Println(obj2)
}
我们把 JSON 数据从 byt 变量反序列化(如解析、解码等等)成名为 dat 的 map/字典对象。这些操作跟其他语言类似,不同的是我们的输入需要是字节数组(不是字符串),对于字典的每个值时需要有类型断言[2]才能读取或访问该值。
当我们处理一个多层嵌套的 JSON 对象时,这些类型断言会让处理变得非常繁琐。
值得注意的是:InterfaceText必须是接口类型!!!
if _,err:=InterfaceText.(T);err{
//..
return
}
解析/序列化为 struct
第二种处理如下:
package main
import (
"encoding/json"
"fmt"
)
type ourData struct {
Num float64 `json:"num"`
Strs []string `json:"strs"`
Obj map[string]map[string]string `json:"obj"`
}
func main() {
byt := []byte(`{
"num":6.13,
"strs":["a","b"],
"obj":{"foo":{"bar":"zip","zap":6}}
}`)
res := ourData{}
json.Unmarshal(byt, &res)
fmt.Println(res.Num)
fmt.Println(res.Strs)
fmt.Println(res.Obj)
}
我们利用 Go struct 的标签功能把 byt 变量中的字节反序列化成一个具体的结构 ourData。
标签是结构体成员定义后跟随的字符串。我们的定义如下:
type ourData struct {
Num float64 `json:"num"`
Strs []string `json:"strs"`
Obj map[string]map[string]string `json:"obj"`
}
你可以看到 Num 成员的 JSON 标签 “num”、Str 成员的 JSON 标签 “strs”、Obj 成员的 JSON 标签 “obj”。这些字符串使用反引号[3]把标签声明为文字串。除了反引号,你也可以使用双引号,但是使用双引号可能会需要一些额外的转义,这样看起来会很凌乱。
type ourData struct {
Num float64 "json:\"num\""
Strs []string "json:\"strs\""
Obj map[string]map[string]string "json:\"obj\""
}
在 struct 的定义中,标签不是必需的。如果你的 struct 中包含了标签,那么它意味着 Go 的 反射 API[4] 可以访问标签的值[5]。Go 中的包可以使用这些标签来进行某些操作。
Go 的 encoding/json
包在反序列化 JSON 成员为具体的 struct 时,通过这些标签来决定每个顶层的 JSON 成员的值。换句话说,当你定义如下的 struct 时:
type ourData struct {
Num float64 `json:"num"`
}
意味着:
当使用 json.Unmarshal 反序列化 JSON 对象为这个 struct 时,取它顶层的 num 成员的值并把它赋给这个 struct 的 Num 成员。
这个操作可以让你的反序列化代码稍微简洁一点,因为程序员不需要对每个成员取值时都显式地调用类型断言。然而,这个仍不是最佳解决方案。
首先 —— 标签只对顶层的成员有效 —— 嵌套的 JSON 需要对应嵌套的类型(如 Obj map[string]map[string]string),因此繁琐的操作仍没有避免。
其次 —— 它假定你的 JSON 结构不会变化。如果你运行上面的程序,你会发现 "zap":6
并没有被赋值到 Obj 成员。你可以通过创建类型 map[string]map[string]interface{}
来处理,但是在这里你又需要进行类型断言了。
这是我第一个 Go 工程遇到的情况,曾让我苦不堪言。
幸运的是,现在我们有了更有效的办法。
SJSON 和 GJSON
Go 内建的 JSON 处理并没有变化,但是已经出现了一些成熟的旨在用起来更简洁高效的处理 JSON 的包。
SJSON[6](写 JSON)和 GJSON[7](读 JSON)是 Josh Baker[8] 开发的两个包,你可以用来读写 JSON 字符串。你可以参考 README 来获取代码实例 —— 下面是使用 GJSON 从 JSON 字符串中获取嵌套的值的示例:
package main import "github.com/tidwall/gjson" const JSON = `{"name":{"first":"Janet","last":"Prichard"},"age":47}` func main() {
value := gjson.Get(json, "name.last")
println(value.String())
}
类似的,下面是使用 SJSON “设置” JSON 字符串中的值返回设置之后的字符串的示例代码:
package main import "github.com/tidwall/sjson" const JSON = `{"name":{"first":"Janet","last":"Prichard"},"age":47}` func main() {
value, _ := sjson.Set(json, "name.last", "Anderson")
println(value)
}
注意点:
1.对于任意层级的json数据,可以使用encoding/json包:
- map[string]interface{} 存储JSON对象
- []interface 存储JSON数组 // 数组中可能是对象或数值
json.Unmarshl
将会把任何合法的JSON数据存储到一个interface{}类型的值,通过使用空接口类型我们可以存储任意值,但是使用这种类型作为值时需要先做一次类型断言。
jsonData := []byte(`{"Name":"Eve","Age":6,"Parents":["Alice","Bob"]}`) var v interface{}
json.Unmarshal(jsonData, &v)
data := v.(map[string]interface{}) for k, v := range data {
switch v := v.(type) {
case string:
fmt.Println(k, v, "(string)")
case float64:
fmt.Println(k, v, "(float64)")
case []interface{}:
fmt.Println(k, "(array):")
for i, u := range v {
fmt.Println(" ", i, u)
}
default:
fmt.Println(k, v, "(unknown)")
}
}
golang之JSON处理的更多相关文章
- 48 【golang】json的效率
本文将主要做如下几方面的测试: 1,构造一个[100]struct的数组,然后来测试它的json编码后的字符串 或者([]byte),首先关心它的功能是否正常: 2,在很早之前,我们在使用golang ...
- Go_14:GoLang中 json、map、struct 之间的相互转化
1. golang 中 json 转 struct <1. 使用 json.Unmarshal 时,结构体的每一项必须是导出项(import field).也就是说结构体的 key 对应的首字母 ...
- Golang 处理 Json(二):解码
golang 编码 json 还比较简单,而解析 json 则非常蛋疼.不像 PHP 一句 json_decode() 就能搞定.之前项目开发中,为了兼容不同客户端的需求,请求的 content-ty ...
- Golang 处理 Json(一):编码
JSON 是一种数据格式描述语言.以 key 和 value 构成的哈系结构,类似 Javascript 中的对象,python 中的字典.通常 json 格式的 key 是字符串,其值可以是任意类型 ...
- GoLang中 json、map、struct 之间的相互转化
1. golang 中 json 转 struct <1. 使用 json.Unmarshal 时,结构体的每一项必须是导出项(import field).也就是说结构体的 key 对应的首字母 ...
- golang解析json报错:invalid character '\x00' after top-level value
golang解析json报错:invalid character '\x00' after top-level value 手动复制字符串:{"files":["c:/t ...
- Golang - 处理json
目录 Golang - 处理json 1. 编码json 2. 解码json Golang - 处理json 1. 编码json 使用json.Marshal()函数可以对一组数据进行JSON格式的编 ...
- golang webservice[ json Martini webframe]
golang webservice[ json Martini webframe] https://github.com/brunoga/go-webservice-sample 自己修改了一下例子, ...
- Golang的json包
encoding/json encoding/json是官方提供的标准json, 实现RFC 7159中定义的JSON编码和解码.使用的时候需要预定义struct,原理是通过reflection和in ...
- Golang解析json的几种方法
Golang解析json的几种方法 概要 使用Golang调用其它平台API接口时总会被多层的json串给恶心到,我记录一下自己解析json的几种方法. 一.自带的json包 func JsonUnm ...
随机推荐
- 【YashanDB知识库】yasdb jdbc驱动集成druid连接池,业务(java)日志中有token IDENTIFIER start异常
问题现象 客户的java日志中有如下异常信息: 问题的风险及影响 对正常的业务流程无影响,但是影响druid的merge sql功能(此功能会将sql语句中的字面量替换为绑定变量,然后将替换以后的sq ...
- webpack高版本抽离css样式报错[已解决]
全局安装的webpack版本是5.51.1,webpack-cli是4.9的版本; 本来想用 extract-text-webpack-plugin 的,但是报错了,查了一下文档 发现,已经不支持新版 ...
- C++面试题整理 2
8. C++11新特性又哪些 自动类型推导auto,智能指指针(share_ptr,unique_ptr等),for循环简化,线程相关的(std::thread/std::mutex),空指针null ...
- vue3 3.3.4
https://cn.vuejs.org/guide/introduction.html#what-is-vue 简介 import { createApp } from 'vue' createAp ...
- MybatisPlus——入门案例
MyBatisPlus MyBatisPlus(简称MP)是基于MyBatis框架基础上开发的增强型工具,旨在简化开发.提高效率 开发方式 基于MyBatis使用MyBatisPlus 基于Sprin ...
- Servlet——Response对象
Response对象 Response 设置响应数据 1.响应行 void setStatus(int sc):设置响应状态码 2.响应头 ...
- BOOST库array使用 类似std库的vector
BOOST库的array, 类似std库的vector. 下图所示书籍的下载地址,我的另一篇博客内有记载: https://www.cnblogs.com/happybirthdaytoyou/p/ ...
- React的useId,现在Vue3.5终于也有了!
前言 React在很早之前的版本中加了useId,用于生成唯一ID.在Vue3.5版本中,终于也有了期待已久的useId.这篇文章来带你搞清楚useId有哪些应用场景,以及他是如何实现的. 关注公众号 ...
- CentOS 7.9安装ElasticSearch7.14.0、ElasticSearch-Head、Kibana、Node14.18.2
CentOS 7.9安装ElasticSearch7.14.0.ElasticSearch-Head.Kibana.Node14.18.2 1.安装文件 1. elasticsearch-7.14 ...
- std::vector::reserve
std::vector::reserve 函数在 C++ 中用于预分配内存,避免在元素增加时多次重新分配内存,从而提高性能. 它最常用于需要频繁向 vector 中添加元素,并且可以预估容器的最终大小 ...