电商商城定制开发go 中解析JSON的三种姿势

背景

电商商城定制开发这是一篇写给0-1电商商城定制开发年新人的文章,电商商城定制开发短平快的教会你如何解析json字符串。

电商商城定制开发一个简单的json字符串

电商商城定制开发假设有如下json字符串:

{   "userName":"admin",   "nick_name":"管理员",   "info":{      "age":18   },   "extra":[      {         "address":"上海市"      },      {         "address":"北京市"      }   ]}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

电商商城定制开发我们来看看,如何在go中解析。

Json解析的3种方式

方式一:成map

示例

anyMap := make(map[string]interface{}, 0)if err := json.Unmarshal([]byte(jsonStr), &anyMap);err!=nil{    // ...}
  • 1
  • 2
  • 3
  • 4

输出如下:

2022/09/13 11:09:13 Unmarshal to map result: map[extra:[map[address:上海市] map[address:北京市]] info:map[age:18] nick_name:管理员 userName:admin]
  • 1

我们看到,key是string,value是interface{},电商商城定制开发所以你取值的时候,需要这样取:

ageVal,ok := anyMap["age"]if ok{    age := ageVal.(int)    log.Println(age)}
  • 1
  • 2
  • 3
  • 4
  • 5

是不是感觉非常麻烦?一是需要判断key是否存在,二是类型转换。

所以,反序列化时这种方式不是很常用,但是序列化的时候,非常方便,用的较多。

扩展

比如在gin框架中,就被大量使用:

type LoginReq struct {   UserName string `json:"user_name"`}func onLogin(ctx *gin.Context) {   req := LoginReq{}   if err := ctx.BindJSON(&req); err != nil {      ctx.Error(err)   }   if req.UserName == "admin" {      ctx.JSON(http.StatusOK, gin.H{"code": 0, "msg": "success"})   } else {      ctx.JSON(http.StatusUnauthorized, gin.H{"code": -1, "msg": "账号错误!"})   }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

看到 gin.H 了吗?实际上就是:

// H is a shortcut for map[string]interface{}type H map[string]interface{}
  • 1
  • 2

所以:

ctx.JSON(http.StatusOK, gin.H{"code": 0, "msg": "success”})
  • 1

等价于:

ctx.JSON(http.StatusOK, map[string]interface{}{"code": 0, "msg": "success"})
  • 1

这样做的好处是,不需要预先定义结构体,直接写在返回结果中,代码更简洁。另外因为map的value是interface,可以进行嵌套返回:

ctx.JSON(http.StatusOK, gin.H{            "status":  gin.H{                "code": http.StatusOK,                "status": "登录成功",            },            "message":"success"        })
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

方式二:反序列化成对象

我们需要先把结构体定义好:

// 嵌套一个对象type Info struct {    Age int `json:"age"`}// 嵌套一个对象数组type Extra struct {    Address string `json:"address"`}// 定义需要反序列化的结构体type UserRequest struct {    Name     string  `json:"userName"`  // 通过tag里面的json,来指定json字符串中该字段的值从那里解析,不需要和字段名一样    NickName string  `json:"nick_name"` // 如果没对应上,解析不了    info     Info    `json:"info"`      // 小写私有的,故反序列化失效,该字段永远为空    Extra    []Extra `json:"extra"`}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

这个是最常用的,通过 json.Unmarshal 把二进制反序列化成对象,通过 json.Marshal() 把对象序列化成json字符串。

    req := UserRequest{}    if err := json.Unmarshal([]byte(jsonStr), &req); err != nil {        panic(err)    }    log.Println("Unmarshal to struct:", req)
  • 1
  • 2
  • 3
  • 4
  • 5

运行后输出:

2022/09/13 11:09:13 Unmarshal to struct: {admin 管理员 {0} [{上海市} {北京市}]}
  • 1

需要注意的是:

  • 字段tag中的名字要和json字符串中的对应,否则解析不到值
  • 字段名不能是小写开头,私有的字段无法将被忽略,也会解析不到值
  • 支持嵌套,由go标准库通过反射自动完成

方式三:复杂json的解析

有时候,一个json非常复杂,或者你只需要取某个字段,那么就可以使用这种方式:

// 方式三:不反序列化,只读取单个key,经常使用。适合特别复杂的json字符串,或者有多种if else结构的场景    userName := gjson.Get(jsonStr, "userName")    nickName := gjson.Get(jsonStr, "nick_name")    age := gjson.Get(jsonStr, "info.age").Int()    // 取得extra数组0位置的对象    address1 := gjson.Get(jsonStr, "extra").Array()[1]    log.Println("get raw value by key:", userName, nickName, age, address1.Get("address"))
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

运行后输出:

2022/09/13 11:09:13 get raw value by key: admin 管理员 18 北京市
  • 1

别忘记,gjson 不是标准库的包,需要使用 go get github.com/tidwall/gjson 安装。

完整代码

package mainimport (    "encoding/json"    "log"    "github.com/tidwall/gjson")// 嵌套一个对象type Info struct {    Age int `json:"age"`}var info = Info{Age: 12}// 嵌套一个对象数组type Extra struct {    Address string `json:"address"`}// 定义需要反序列化的结构体type UserRequest struct {    Name     string  `json:"userName"`  // 通过tag里面的json,来指定json字符串中该字段的值从那里解析,不需要和字段名一样    NickName string  `json:"nick_name"` // 如果没对应上,解析不了    info     Info    `json:"info"`      // 小写私有的,故反序列化失效,该字段永远为空    Extra    []Extra `json:"extra"`}func main() {    jsonStr := `    {        "userName":"admin",        "nick_name":"管理员",        "info":{           "age":18        },        "extra":[           {              "address":"上海市"           },           {              "address":"北京市"           }        ]     }`    // 方式一:序列化成map,经常使用    anyMap := make(map[string]interface{}, 0)    if err := json.Unmarshal([]byte(jsonStr), &anyMap); err != nil {        panic(err)    }    log.Println("Unmarshal to map result:", anyMap)    // 方式二:反序列化成对象,经常使用    req := UserRequest{}    if err := json.Unmarshal([]byte(jsonStr), &req); err != nil {        panic(err)    }    log.Println("Unmarshal to struct:", req)    // 方式三:不反序列化,只读取单个key,经常使用。适合特别复杂的json字符串,或者有多种if else结构的场景    userName := gjson.Get(jsonStr, "userName")    nickName := gjson.Get(jsonStr, "nick_name")    age := gjson.Get(jsonStr, "info.age").Int()    // 取得extra数组0位置的对象    address1 := gjson.Get(jsonStr, "extra").Array()[1]    log.Println("get raw value by key:", userName, nickName, age, address1.Get("address"))}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69

运行后输出:

2022/09/13 11:09:13 Unmarshal to map result: map[extra:[map[address:上海市] map[address:北京市]] info:map[age:18] nick_name:管理员 userName:admin]2022/09/13 11:09:13 Unmarshal to struct: {admin 管理员 {0} [{上海市} {北京市}]}2022/09/13 11:09:13 get raw value by key: admin 管理员 18 北京市
  • 1
  • 2
  • 3

总结

本文介绍了解析json的3种方式:

  • 直接解析成 map[string]interface{}
  • 解析成对象
  • 读取单个key

这3种方式没有好坏之分,只看具体的场景,灵活使用即可。唯一不变的是,都是通过 json.Unmarshal 把二进制反序列化成对象,通过 json.Marshal() 把对象序列化成json字符串。

加餐:什么是序列化?

百科中说:序列化 (Serialization)是将对象的状态信息转换为可以存储或传输的形式的过程。

通俗点说就是因为网络只能发送二进制流,而我们代码中的数据又是保存在各种对象中。在C/S(客户端/服务器)架构中,客户端传递数据给服务器势必就要进行2次转换。

  • 客户端:发送之前,要把对象转换为二进制字节流。
  • 服务器:接收到二进制流之后,要转换为对象。

这种从对象到二进制流的过程叫序列化(Serialization),反过来从二进制流转换成对象就叫反序列化。

References:

——————传说中的分割线——————
大家好,我目前已从C++后端转型为Golang后端,可以订阅关注下《Go和分布式IM》公众号,获取一名转型萌新Gopher的心路成长历程和升级打怪技巧。

网站建设定制开发 软件系统开发定制 定制软件开发 软件开发定制 定制app开发 app开发定制 app开发定制公司 电商商城定制开发 定制小程序开发 定制开发小程序 客户管理系统开发定制 定制网站 定制开发 crm开发定制 开发公司 小程序开发定制 定制软件 收款定制开发 企业网站定制开发 定制化开发 android系统定制开发 定制小程序开发费用 定制设计 专注app软件定制开发 软件开发定制定制 知名网站建设定制 软件定制开发供应商 应用系统定制开发 软件系统定制开发 企业管理系统定制开发 系统定制开发