Golang的json解析--Gjson库的使用举例

目录

简介

安装

原生的json解析 

Gjson使用举例

基本使用

键路径

使用示例

其他资源

简介

在 Golang 中,解析 JSON 数据是一项非常常见的任务。Go提供了标准的JSON包,可以轻松地将JSON数据序列化和反序列化。但是,在使用标准JSON包解析大型复杂JSON结构时,可能存在些许不足,例如代码冗余,性能瓶颈等问题。针对这些问题,目前有许多优秀的JSON解析框架,GJSON是其中一个很不错的选择。

GJSON 是一个 Go 包,提供了一种 高效 和 简单 的方式来从 JSON 文档中提取值。它具有如 一行检索点号路径语法迭代 和 解析 JSON 行 等特性。

本文将详细讲解如何使用GJSON框架解析JSON数据。

安装

GJSON模块可以通过go get命令来安装。

go get -u github.com/tidwall/gjson

原生的json解析 

以解析豆瓣接口https://api.douban.com/v2/movie/new_movies,返回的数据如下:

{
  "subjects": [
    {
      "rating": {
        "max": 10,
        "average": 7.1,
        "details": {
          "1": 16.0,
          "3": 135.0,
          "2": 56.0,
          "5": 102.0,
          "4": 122.0
        },
        "stars": "35",
        "min": 0
      },
      "genres": [
        "\u5267\u60c5",
        "\u559c\u5267",
        "\u52a8\u4f5c"
      ],
      "title": "\u76df\u519b\u6562\u6b7b\u961f",
      "casts": [
        {
          "avatars": {
            "small": "https://img9.doubanio.com\/view\/celebrity\/m\/public\/p1371934661.95.jpg",
            "large": "https://img9.doubanio.com\/view\/celebrity\/m\/public\/p1371934661.95.jpg",
            "medium": "https://img9.doubanio.com\/view\/celebrity\/m\/public\/p1371934661.95.jpg"
          },
          "name_en": "Henry Cavill",
          "name": "\u4ea8\u5229\u00b7\u5361\u7ef4\u5c14",
          "alt": "https:\/\/movie.douban.com\/celebrity\/1044713\/",
          "id": "1044713"
        },
        {
          "avatars": {
            "small": "https://img3.doubanio.com\/view\/celebrity\/m\/public\/p1607496406.37.jpg",
            "large": "https://img3.doubanio.com\/view\/celebrity\/m\/public\/p1607496406.37.jpg",
            "medium": "https://img3.doubanio.com\/view\/celebrity\/m\/public\/p1607496406.37.jpg"
          },
          "name_en": "Eiza Gonz\u00e1lez",
          "name": "\u827e\u838e\u00b7\u5188\u8428\u96f7\u65af",
          "alt": "https:\/\/movie.douban.com\/celebrity\/1233270\/",
          "id": "1233270"
        }
      ],
      "durations": [
        "120\u5206\u949f"
      ],
      "collect_count": 25322,
      "mainland_pubdate": "2024-05-24",
      "has_video": false,
      "original_title": "The Ministry of Ungentlemanly Warfare",
      "subtype": "movie",
      "directors": [
        {
          "avatars": {
            "small": "https://img1.doubanio.com\/view\/celebrity\/m\/public\/p47189.jpg",
            "large": "https://img1.doubanio.com\/view\/celebrity\/m\/public\/p47189.jpg",
            "medium": "https://img1.doubanio.com\/view\/celebrity\/m\/public\/p47189.jpg"
          },
          "name_en": "Guy Ritchie",
          "name": "\u76d6\u00b7\u91cc\u5947",
          "alt": "https:\/\/movie.douban.com\/celebrity\/1025148\/",
          "id": "1025148"
        }
      ],
      "pubdates": [
        "2024-04-18(\u4e2d\u56fd\u9999\u6e2f)",
        "2024-04-19(\u7f8e\u56fd)",
        "2024-05-24(\u4e2d\u56fd\u5927\u9646)"
      ],
      "year": "2024",
      "images": {
        "small": "https://img9.doubanio.com\/view\/photo\/s_ratio_poster\/public\/p2908456064.jpg",
        "large": "https://img9.doubanio.com\/view\/photo\/s_ratio_poster\/public\/p2908456064.jpg",
        "medium": "https://img9.doubanio.com\/view\/photo\/s_ratio_poster\/public\/p2908456064.jpg"
      },
      "alt": "https:\/\/movie.douban.com\/subject\/34971728\/",
      "id": "34971728"
    }
 ],
  "title": "\u8c46\u74e3\u7535\u5f71\u65b0\u7247\u699c"
}

使用golang原始的方式,将响应体JSON反序列化为一个字典(map),以便于访问其中的数据。

body, err := io.ReadAll(res.Body)
	if err != nil {
		l.Errorf("Failed to read response body:", err)
		return nil, err
	}
	//格式化输出json
	//var str bytes.Buffer
	//_ = json.Indent(&str, []byte(body), "", "    ")
	//l.Debugf("formated: ", str.String())
	var keyVal map[string]interface{}
	err = json.Unmarshal(body, &keyVal)
	if err != nil {
		l.Errorf("Failed to extract key value:", err)
	}
	//l.Debug(keyValue)
	var hot types.HotItem
	var responseData []types.HotItem
	list_, ok := keyVal["subjects"].([]interface{})
	if ok {
		for _, item := range list_ {
			itemMap, ok := item.(map[string]interface{})
			if ok {
				//l.Debug(itemMap)
				hot.Id = itemMap["id"].(string)
				hot.Title = itemMap["title"].(string)
				tmp := itemMap["images"].(map[string]interface{})
				hot.Cover = l.svcCtx.Config.MvConf.Referer + tmp["small"].(string)
				hot.Rate = itemMap["rating"].(map[string]interface{})["average"].(float64)
			}
			responseData = append(responseData, hot)
		}
	}

 示例如下:

func (l *HotMovieLogic) HotMovie(req *types.HotMovieReq) (resp *types.HotMovieResp, err error) {
	// todo: add your logic here and delete this line
	type Request struct {
		Req    types.HotMovieReq
		ApiKey string `json:"apikey"`
	}
	req_ := Request{
		Req:    *req,
		ApiKey: l.svcCtx.Config.MvConf.ApiKey,
	}
	l.Debug(req_)
	url := l.svcCtx.Config.MvConf.BaseUrl + "/movie/in_theaters"
	res, err_ := httpc.Do(l.ctx, http.MethodPost, url, req_)
	if err_ != nil {
		l.Error(err_)
		return nil, err_
	}
	defer res.Body.Close()
	body, err := io.ReadAll(res.Body)
	if err != nil {
		l.Errorf("Failed to read response body:", err)
		return nil, err
	}
	//格式化输出json
	//var str bytes.Buffer
	//_ = json.Indent(&str, []byte(body), "", "    ")
	//l.Debugf("formated: ", str.String())
	var keyVal map[string]interface{}
	err = json.Unmarshal(body, &keyVal)
	if err != nil {
		l.Errorf("Failed to extract key value:", err)
	}
	//l.Debug(keyValue)
	var hot types.HotItem
	var responseData []types.HotItem
	list_, ok := keyVal["subjects"].([]interface{})
	if ok {
		for _, item := range list_ {
			itemMap, ok := item.(map[string]interface{})
			if ok {
				//l.Debug(itemMap)
				hot.Id = itemMap["id"].(string)
				hot.Title = itemMap["title"].(string)
				tmp := itemMap["images"].(map[string]interface{})
				hot.Cover = l.svcCtx.Config.MvConf.Referer + tmp["small"].(string)
				hot.Rate = itemMap["rating"].(map[string]interface{})["average"].(float64)
			}
			responseData = append(responseData, hot)
		}
	}
	//t := reflect.TypeOf(keyVal["count"])
	//l.Debugf("Type: %v\n", t)
	resp = &types.HotMovieResp{
		Code:    0,
		Message: res.Status,
		Data:    responseData,
		Count:   int(keyVal["count"].(float64)),
		Start:   int(keyVal["start"].(float64)),
		Total:   int(keyVal["total"].(float64)),
		Title:   keyVal["title"].(string),
	}
	return resp, nil
}

Gjson使用举例

基本使用

package main

import (
  "fmt"

  "github.com/tidwall/gjson"
)

func main() {
  json := `{"name":{"first":"li","last":"dj"},"age":18}`
  lastName := gjson.Get(json, "name.last")
  fmt.Println("last name:", lastName.String())

  age := gjson.Get(json, "age")
  fmt.Println("age:", age.Int())

  email := gjson.Get(json, "email")
  // json 中不存在该字段时,String为空串,Int为0,即返回想要转换的类型的零值
  fmt.Println("email:", email.String())
}

使用很简单,只需要传入 JSON 串和要读取的键路径即可。注意一点细节,因为gjson.Get()函数实际上返回的是gjson.Result类型,我们要调用其相应的方法进行转换对应的类型。如上面的String()Int()方法。 

获取值

Get 函数搜索 JSON 中指定的路径。路径以点号分隔,如 "name.last" 或 "age"。一旦找到值,会立即返回。

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())
}

键路径

键路径实际上是以.分隔的一系列键。gjson支持在键中包含通配符*和?,*匹配任意多个字符,?匹配单个字符,例如ca*可以匹配cat、cate、cake等以ca开头的键,ca?只能匹配cat、cap等以ca开头且后面只有一个字符的键。

数组使用键名 + . + 索引(索引从 0 开始)的方式读取元素,如果键pets对应的值是一个数组,那么pets.0读取数组的第一个元素,pets.1读取第二个元素。

数组长度使用键名 + . + #获取,例如pets.#返回数组pets的长度。

如果键名中出现.,那么需要使用\进行转义。

路径语法

以下是对路径语法的快速概述,更多信息请参阅 GJSON 语法

路径是通过点号分隔的一系列键。键可能包含特殊通配符 '*' 和 '?'。用索引作为键访问数组值。使用 '#' 字符获取数组元素的数量或访问子路径。点号和通配符可以用 '\' 转义。

{
  "name": {"first": "Tom", "last": "Anderson"},
  "age":37,
  "children": ["Sara","Alex","Jack"],
  "fav.movie": "Deer Hunter",
  "friends": [
    {"first": "Dale", "last": "Murphy", "age": 44, "nets": ["ig", "fb", "tw"]},
    {"first": "Roger", "last": "Craig", "age": 68, "nets": ["fb", "tw"]},
    {"first": "Jane", "last": "Murphy", "age": 47, "nets": ["ig", "tw"]}
  ]
}

取值示例: 

"name.last"          >> "Anderson"
"age"                >> 37
"children"           >> ["Sara","Alex","Jack"]
"children.#"         >> 3
"children.1"         >> "Alex"
"child*.2"           >> "Jack"
"c?ildren.0"         >> "Sara"
"fav\.movie"         >> "Deer Hunter"
"friends.#.first"    >> ["Dale","Roger","Jane"]
"friends.1.last"     >> "Craig"

结果类型

GJSON 支持 JSON 类型 stringnumberbool 和 null。数组和对象以它们的原始 JSON 类型返回。

Result 类型持有这些之一:

bool, 对应 JSON 布尔值
float64, 对应 JSON 数字
string, 对应 JSON 字符串字面量
nil, 对应 JSON null

获取对象数组中的元素

var nitem types.NewItem
	var responseData []types.NewItem
	list_ := gjson.GetBytes(body, "subjects").Array()
	for _, item := range list_ {
		nitem.Id = item.Get("id").String()
		nitem.Title = item.Get("title").String()
		nitem.Cover = l.svcCtx.Config.MvConf.Referer + item.Get("images.small").String()
		nitem.Rate = item.Get("rating.average").Float()
		nitem.Pubdate = item.Get("pubdates.0").String()
		responseData = append(responseData, nitem)
	}

使用示例

解析豆瓣影视接口返回的数据:

func (l *NewMovieLogic) NewMovie(req *types.NewMovieReq) (resp *types.NewMovieResp, err error) {
	type Request struct {
		Req    types.NewMovieReq
		ApiKey string `json:"apikey"`
	}
	req_ := Request{
		Req:    *req,
		ApiKey: l.svcCtx.Config.MvConf.ApiKey,
	}
	l.Debug(req_)
	url := l.svcCtx.Config.MvConf.BaseUrl + "/movie/new_movies"
	res, err_ := httpc.Do(l.ctx, http.MethodPost, url, req_)
	if err_ != nil {
		l.Error(err_)
		return nil, err_
	}
	defer res.Body.Close()
	body, err := io.ReadAll(res.Body)
	if err != nil {
		l.Errorf("Failed to read response body:", err)
		return nil, err
	}
	//格式化输出json
	//var str bytes.Buffer
	//_ = json.Indent(&str, []byte(body), "", "    ")
	//l.Debugf("formated: ", str.String())
	//l.Debug(keyValue)
	var nitem types.NewItem
	var responseData []types.NewItem
	list_ := gjson.GetBytes(body, "subjects").Array()
	for _, item := range list_ {
		nitem.Id = item.Get("id").String()
		nitem.Title = item.Get("title").String()
		nitem.Cover = l.svcCtx.Config.MvConf.Referer + item.Get("images.small").String()
		nitem.Rate = item.Get("rating.average").Float()
		nitem.Pubdate = item.Get("pubdates.0").String()
		responseData = append(responseData, nitem)
	}
	//t := reflect.TypeOf(keyVal["count"])
	//l.Debugf("Type: %v\n", t)
	if len(list_) != 0 {
		resp = &types.NewMovieResp{
			Code:    0,
			Message: res.Status,
			Data:    responseData,
			Count:   len(list_),
			Start:   0,
			Total:   len(list_),
			Title:   gjson.GetBytes(body, "title").String(),
		}
	} else {
		resp = &types.NewMovieResp{
			Code:    0,
			Message: res.Status,
			Data:    responseData,
			Count:   0,
			Start:   0,
			Total:   0,
			Title:   "",
		}
	}
	return resp, nil
}

其他资源

Go 语言 gjson对Json数据进行操作_go gjson-CSDN博客

34.Go操作JSON利器之gjson包_golang gjson-CSDN博客

GitCode - 全球开发者的开源社区,开源代码托管平台

  • 19
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

特立独行的猫a

您的鼓励是我的创作动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值