0


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

简介

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

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

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

安装

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

  1. go get -u github.com/tidwall/gjson

原生的json解析

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

  1. {
  2. "subjects": [
  3. {
  4. "rating": {
  5. "max": 10,
  6. "average": 7.1,
  7. "details": {
  8. "1": 16.0,
  9. "3": 135.0,
  10. "2": 56.0,
  11. "5": 102.0,
  12. "4": 122.0
  13. },
  14. "stars": "35",
  15. "min": 0
  16. },
  17. "genres": [
  18. "\u5267\u60c5",
  19. "\u559c\u5267",
  20. "\u52a8\u4f5c"
  21. ],
  22. "title": "\u76df\u519b\u6562\u6b7b\u961f",
  23. "casts": [
  24. {
  25. "avatars": {
  26. "small": "https://img9.doubanio.com\/view\/celebrity\/m\/public\/p1371934661.95.jpg",
  27. "large": "https://img9.doubanio.com\/view\/celebrity\/m\/public\/p1371934661.95.jpg",
  28. "medium": "https://img9.doubanio.com\/view\/celebrity\/m\/public\/p1371934661.95.jpg"
  29. },
  30. "name_en": "Henry Cavill",
  31. "name": "\u4ea8\u5229\u00b7\u5361\u7ef4\u5c14",
  32. "alt": "https:\/\/movie.douban.com\/celebrity\/1044713\/",
  33. "id": "1044713"
  34. },
  35. {
  36. "avatars": {
  37. "small": "https://img3.doubanio.com\/view\/celebrity\/m\/public\/p1607496406.37.jpg",
  38. "large": "https://img3.doubanio.com\/view\/celebrity\/m\/public\/p1607496406.37.jpg",
  39. "medium": "https://img3.doubanio.com\/view\/celebrity\/m\/public\/p1607496406.37.jpg"
  40. },
  41. "name_en": "Eiza Gonz\u00e1lez",
  42. "name": "\u827e\u838e\u00b7\u5188\u8428\u96f7\u65af",
  43. "alt": "https:\/\/movie.douban.com\/celebrity\/1233270\/",
  44. "id": "1233270"
  45. }
  46. ],
  47. "durations": [
  48. "120\u5206\u949f"
  49. ],
  50. "collect_count": 25322,
  51. "mainland_pubdate": "2024-05-24",
  52. "has_video": false,
  53. "original_title": "The Ministry of Ungentlemanly Warfare",
  54. "subtype": "movie",
  55. "directors": [
  56. {
  57. "avatars": {
  58. "small": "https://img1.doubanio.com\/view\/celebrity\/m\/public\/p47189.jpg",
  59. "large": "https://img1.doubanio.com\/view\/celebrity\/m\/public\/p47189.jpg",
  60. "medium": "https://img1.doubanio.com\/view\/celebrity\/m\/public\/p47189.jpg"
  61. },
  62. "name_en": "Guy Ritchie",
  63. "name": "\u76d6\u00b7\u91cc\u5947",
  64. "alt": "https:\/\/movie.douban.com\/celebrity\/1025148\/",
  65. "id": "1025148"
  66. }
  67. ],
  68. "pubdates": [
  69. "2024-04-18(\u4e2d\u56fd\u9999\u6e2f)",
  70. "2024-04-19(\u7f8e\u56fd)",
  71. "2024-05-24(\u4e2d\u56fd\u5927\u9646)"
  72. ],
  73. "year": "2024",
  74. "images": {
  75. "small": "https://img9.doubanio.com\/view\/photo\/s_ratio_poster\/public\/p2908456064.jpg",
  76. "large": "https://img9.doubanio.com\/view\/photo\/s_ratio_poster\/public\/p2908456064.jpg",
  77. "medium": "https://img9.doubanio.com\/view\/photo\/s_ratio_poster\/public\/p2908456064.jpg"
  78. },
  79. "alt": "https:\/\/movie.douban.com\/subject\/34971728\/",
  80. "id": "34971728"
  81. }
  82. ],
  83. "title": "\u8c46\u74e3\u7535\u5f71\u65b0\u7247\u699c"
  84. }

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

  1. body, err := io.ReadAll(res.Body)
  2. if err != nil {
  3. l.Errorf("Failed to read response body:", err)
  4. return nil, err
  5. }
  6. //格式化输出json
  7. //var str bytes.Buffer
  8. //_ = json.Indent(&str, []byte(body), "", " ")
  9. //l.Debugf("formated: ", str.String())
  10. var keyVal map[string]interface{}
  11. err = json.Unmarshal(body, &keyVal)
  12. if err != nil {
  13. l.Errorf("Failed to extract key value:", err)
  14. }
  15. //l.Debug(keyValue)
  16. var hot types.HotItem
  17. var responseData []types.HotItem
  18. list_, ok := keyVal["subjects"].([]interface{})
  19. if ok {
  20. for _, item := range list_ {
  21. itemMap, ok := item.(map[string]interface{})
  22. if ok {
  23. //l.Debug(itemMap)
  24. hot.Id = itemMap["id"].(string)
  25. hot.Title = itemMap["title"].(string)
  26. tmp := itemMap["images"].(map[string]interface{})
  27. hot.Cover = l.svcCtx.Config.MvConf.Referer + tmp["small"].(string)
  28. hot.Rate = itemMap["rating"].(map[string]interface{})["average"].(float64)
  29. }
  30. responseData = append(responseData, hot)
  31. }
  32. }

示例如下:

  1. func (l *HotMovieLogic) HotMovie(req *types.HotMovieReq) (resp *types.HotMovieResp, err error) {
  2. // todo: add your logic here and delete this line
  3. type Request struct {
  4. Req types.HotMovieReq
  5. ApiKey string `json:"apikey"`
  6. }
  7. req_ := Request{
  8. Req: *req,
  9. ApiKey: l.svcCtx.Config.MvConf.ApiKey,
  10. }
  11. l.Debug(req_)
  12. url := l.svcCtx.Config.MvConf.BaseUrl + "/movie/in_theaters"
  13. res, err_ := httpc.Do(l.ctx, http.MethodPost, url, req_)
  14. if err_ != nil {
  15. l.Error(err_)
  16. return nil, err_
  17. }
  18. defer res.Body.Close()
  19. body, err := io.ReadAll(res.Body)
  20. if err != nil {
  21. l.Errorf("Failed to read response body:", err)
  22. return nil, err
  23. }
  24. //格式化输出json
  25. //var str bytes.Buffer
  26. //_ = json.Indent(&str, []byte(body), "", " ")
  27. //l.Debugf("formated: ", str.String())
  28. var keyVal map[string]interface{}
  29. err = json.Unmarshal(body, &keyVal)
  30. if err != nil {
  31. l.Errorf("Failed to extract key value:", err)
  32. }
  33. //l.Debug(keyValue)
  34. var hot types.HotItem
  35. var responseData []types.HotItem
  36. list_, ok := keyVal["subjects"].([]interface{})
  37. if ok {
  38. for _, item := range list_ {
  39. itemMap, ok := item.(map[string]interface{})
  40. if ok {
  41. //l.Debug(itemMap)
  42. hot.Id = itemMap["id"].(string)
  43. hot.Title = itemMap["title"].(string)
  44. tmp := itemMap["images"].(map[string]interface{})
  45. hot.Cover = l.svcCtx.Config.MvConf.Referer + tmp["small"].(string)
  46. hot.Rate = itemMap["rating"].(map[string]interface{})["average"].(float64)
  47. }
  48. responseData = append(responseData, hot)
  49. }
  50. }
  51. //t := reflect.TypeOf(keyVal["count"])
  52. //l.Debugf("Type: %v\n", t)
  53. resp = &types.HotMovieResp{
  54. Code: 0,
  55. Message: res.Status,
  56. Data: responseData,
  57. Count: int(keyVal["count"].(float64)),
  58. Start: int(keyVal["start"].(float64)),
  59. Total: int(keyVal["total"].(float64)),
  60. Title: keyVal["title"].(string),
  61. }
  62. return resp, nil
  63. }

Gjson使用举例

基本使用

  1. package main
  2. import (
  3. "fmt"
  4. "github.com/tidwall/gjson"
  5. )
  6. func main() {
  7. json := `{"name":{"first":"li","last":"dj"},"age":18}`
  8. lastName := gjson.Get(json, "name.last")
  9. fmt.Println("last name:", lastName.String())
  10. age := gjson.Get(json, "age")
  11. fmt.Println("age:", age.Int())
  12. email := gjson.Get(json, "email")
  13. // json 中不存在该字段时,String为空串,Int为0,即返回想要转换的类型的零值
  14. fmt.Println("email:", email.String())
  15. }

使用很简单,只需要传入

  1. JSON

串和要读取的键路径即可。注意一点细节,因为

  1. gjson.Get()

函数实际上返回的是

  1. gjson.Result

类型,我们要调用其相应的方法进行转换对应的类型。如上面的

  1. String()

  1. Int()

方法。

获取值

  1. Get

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

  1. package main
  2. import "github.com/tidwall/gjson"
  3. const json = `{"name":{"first":"Janet","last":"Prichard"},"age":47}`
  4. func main() {
  5. value := gjson.Get(json, "name.last")
  6. println(value.String())
  7. }

键路径

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

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

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

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

路径语法

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

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

  1. {
  2. "name": {"first": "Tom", "last": "Anderson"},
  3. "age":37,
  4. "children": ["Sara","Alex","Jack"],
  5. "fav.movie": "Deer Hunter",
  6. "friends": [
  7. {"first": "Dale", "last": "Murphy", "age": 44, "nets": ["ig", "fb", "tw"]},
  8. {"first": "Roger", "last": "Craig", "age": 68, "nets": ["fb", "tw"]},
  9. {"first": "Jane", "last": "Murphy", "age": 47, "nets": ["ig", "tw"]}
  10. ]
  11. }

取值示例:

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

结果类型

GJSON 支持 JSON 类型

  1. string

,

  1. number

,

  1. bool

  1. null

。数组和对象以它们的原始 JSON 类型返回。

  1. Result

类型持有这些之一:

  1. bool, 对应 JSON 布尔值
  2. float64, 对应 JSON 数字
  3. string, 对应 JSON 字符串字面量
  4. nil, 对应 JSON null

获取对象数组中的元素

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

使用示例

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

  1. func (l *NewMovieLogic) NewMovie(req *types.NewMovieReq) (resp *types.NewMovieResp, err error) {
  2. type Request struct {
  3. Req types.NewMovieReq
  4. ApiKey string `json:"apikey"`
  5. }
  6. req_ := Request{
  7. Req: *req,
  8. ApiKey: l.svcCtx.Config.MvConf.ApiKey,
  9. }
  10. l.Debug(req_)
  11. url := l.svcCtx.Config.MvConf.BaseUrl + "/movie/new_movies"
  12. res, err_ := httpc.Do(l.ctx, http.MethodPost, url, req_)
  13. if err_ != nil {
  14. l.Error(err_)
  15. return nil, err_
  16. }
  17. defer res.Body.Close()
  18. body, err := io.ReadAll(res.Body)
  19. if err != nil {
  20. l.Errorf("Failed to read response body:", err)
  21. return nil, err
  22. }
  23. //格式化输出json
  24. //var str bytes.Buffer
  25. //_ = json.Indent(&str, []byte(body), "", " ")
  26. //l.Debugf("formated: ", str.String())
  27. //l.Debug(keyValue)
  28. var nitem types.NewItem
  29. var responseData []types.NewItem
  30. list_ := gjson.GetBytes(body, "subjects").Array()
  31. for _, item := range list_ {
  32. nitem.Id = item.Get("id").String()
  33. nitem.Title = item.Get("title").String()
  34. nitem.Cover = l.svcCtx.Config.MvConf.Referer + item.Get("images.small").String()
  35. nitem.Rate = item.Get("rating.average").Float()
  36. nitem.Pubdate = item.Get("pubdates.0").String()
  37. responseData = append(responseData, nitem)
  38. }
  39. //t := reflect.TypeOf(keyVal["count"])
  40. //l.Debugf("Type: %v\n", t)
  41. if len(list_) != 0 {
  42. resp = &types.NewMovieResp{
  43. Code: 0,
  44. Message: res.Status,
  45. Data: responseData,
  46. Count: len(list_),
  47. Start: 0,
  48. Total: len(list_),
  49. Title: gjson.GetBytes(body, "title").String(),
  50. }
  51. } else {
  52. resp = &types.NewMovieResp{
  53. Code: 0,
  54. Message: res.Status,
  55. Data: responseData,
  56. Count: 0,
  57. Start: 0,
  58. Total: 0,
  59. Title: "",
  60. }
  61. }
  62. return resp, nil
  63. }

其他资源

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

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

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


本文转载自: https://blog.csdn.net/qq8864/article/details/139652449
版权归原作者 特立独行的猫a 所有, 如有侵权,请联系我们删除。

“Golang的json解析--Gjson库的使用举例”的评论:

还没有评论