0
前言,最近项目中需要动态解析json字符串,但是golang标准库提供的json需要事先定义好结构体,不太灵活,因此需要调研不依赖于结构体的json库

目前golang开源json库各自的优缺点

  • encoding/json, 官方自带的, 文档最多, 易用性差, 性能差
  • go-simplejson, gabs, jason等衍生包, 简单且易用, 易于阅读, 便于维护, 但性能最差
  • easyjson, ffjson此类包, 适合固定结构的json, 易用性一般, 维护成本高, 性能特别好
  • jsonparser 适合动态和固定结构的json, 简单且易用, 维护成本低, 性能极好

以性能的高低排序: jsonparser > easyjson > encoding/json > go-simplejson, gabs, jason

性能测试, 可见jsonparser的github
buger/jsonparser


golang simplejson使用笔记

  • 介绍:

golang标准库的json需要预先定义好结构体,然后才能将json字符串转化为golang的结构体;simplejson这个开源的库可以在不知道json字符串具体结构的情况下进行编码和解码

  • 使用:

import (
 simplejson "github.com/bitly/go-simplejson"
)
func case1() {
 //初始化*simpleJson.Json对象
 sj, err := simplejson.NewJson([]byte(jsonStr))
 var v *simpleJson.Json
 
 //获取字段,如果有多级,可以层层嵌套获取
 v = sj.Get(字段名1).Get(字段名2)
 
 //将v的值转化为具体类型的值,MustXXX方法一定可以转化成功
 //若转化不成功,则转化为该类型的零值
 result := v.MustString()
}
func case2() {
 //检查某个字段是否存在 
 _, ok := js.Get("字段名1").CheckGet("字段名2") 
 if ok { 
 fmt.Println("存在!") 
 } else { 
 fmt.Println("不存在") 
 }
 
}
  • 总结:

虽然simplejson可以转化一个未知的json,但想要获取到具体的值,仍然需要知道它的类型,这样最后一步转换才能成功

golang jsonparser使用笔记

import "github.com/buger/jsonparser"
...
data := []byte(`{
 "person": {
 "name": {
 "first": "Leonid",
 "last": "Bugaev",
 "fullName": "Leonid Bugaev"
 },
 "github": {
 "handle": "buger",
 "followers": 109
 },
 "avatars": [
 { "url": "https://avatars1.githubusercontent.com/u/14009?v=3&s=460", "type": "thumbnail" }
 ]
 },
 "company": {
 "name": "Acme"
 }
}`)
//根据层级关系取特定的字段名对应的值
jsonparser.Get(data, "person", "name", "fullName")
//如果知道需要取的目标字段的值的类型,可以使用详细的GetXXX方法
jsonparser.GetInt(data, "person", "github", "followers")
// When you try to get object, it will return you []byte slice pointer to data containing it
// In `company` it will be `{"name": "Acme"}`
jsonparser.Get(data, "company")
// If the key doesn't exist it will throw an error
var size int64
if value, err := jsonparser.GetInt(data, "company", "size"); err == nil {
 size = value
}
// You can use `ArrayEach` helper to iterate items [item1, item2 .... itemN]
jsonparser.ArrayEach(data, func(value []byte, dataType jsonparser.ValueType, offset int, err error) {
 fmt.Println(jsonparser.Get(value, "url"))
}, "person", "avatars")
// Or use can access fields by index!
jsonparser.GetInt("person", "avatars", "[0]", "url")
// You can use `ObjectEach` helper to iterate objects { "key1":object1, "key2":object2, .... "keyN":objectN }
jsonparser.ObjectEach(data, func(key []byte, value []byte, dataType jsonparser.ValueType, offset int) error {
 fmt.Printf("Key: '%s'\n Value: '%s'\n Type: %s\n", string(key), string(value), dataType)
 return nil
}, "person", "name")
// The most efficient way to extract multiple keys is `EachKey`
paths := [][]string{
 []string{"person", "name", "fullName"},
 []string{"person", "avatars", "[0]", "url"},
 []string{"company", "url"},
}
jsonparser.EachKey(data, func(idx int, value []byte, vt jsonparser.ValueType, err error){
 switch idx {
 case 0: // []string{"person", "name", "fullName"}
 ...
 case 1: // []string{"person", "avatars", "[0]", "url"}
 ...
 case 2: // []string{"company", "url"},
 ...
 }
}, paths...)

详细解读

如果只是单纯使用Get,返回值有四个,分别是:

value []byte, dataType ValueType, offset int, err error

可以发现,value是一个[]byte类型,实际取值需要手动将其转换为对应的类型;

第二个参数是自动推导的类型,支持的推导类型有:

const (
 NotExist = ValueType(iota)
 String
 Number
 Object
 Array
 Boolean
 Null 
 Unknown 
 )
  • 比较有意思的是,它支持通过索引下标去获取数组数据
ret, err := jsonparser.GetInt(data, "person", "avatars", "[0]", "index")
  • 可以遍历一个key下的所有key的值
err = jsonparser.ObjectEach(data, func(key []byte, value []byte, dataType jsonparser.ValueType, offset int) error {
 fmt.Printf("Key: '%s'\t Value: '%s'\t Type: %s\n", string(key), string(value), dataType) 
 return nil 
 }, "person", "name")

结果:

Key: 'first' Value: 'Leonid' Type: string
Key: 'last' Value: 'Bugaev' Type: string
Key: 'fullName' Value: 'Leonid Bugaev' Type: string
  • 若遍历的key的值不是一个基础类型的数据,例如:
err = jsonparser.ObjectEach(data, func(key []byte, value []byte, dataType jsonparser.ValueType, offset int) error {
 fmt.Printf("Key: '%s'\t Value: '%s'\t Type: %s\n", string(key), string(value), dataType)
 return nil 
 }, "person") 

结果:

Key: 'name' Value: '{
 "first": "Leonid",
 "last": "Bugaev",
 "fullName": "Leonid Bugaev"
 }' Type: object
Key: 'github' Value: '{
 "handle": "buger",
 "followers": 109
 }' Type: object
Key: 'avatars' Value: '[
 {"index":100},
 { "url": "https://avatars1.githubusercontent.com/u/14009?v=3&s=460", "type": "thumbnail" }, 
 { "url1": "https://avatars2.githubusercontent.com/u/14009?v=3&s=460", "type": "thumbnail" } 
 ]' Type: array
  • 若需要遍历一个数组:
_, err = jsonparser.ArrayEach(data, func(value []byte, dataType jsonparser.ValueType, offset int, e error) {
 fmt.Printf("each, Value: '%s'\t Type: %s\n", string(value), dataType)
 }, "person", "avatars") 

结果

each, Value: '{"index":100}' Type: object
each, Value: '{ "url": "https://avatars1.githubusercontent.com/u/14009?v=3&s=460", "type": "thumbnail" }' Type: object
each, Value: '{ "url1": "https://avatars2.githubusercontent.com/u/14009?v=3&s=460", "type": "thumbnail" }' Type: object

byte
106 声望13 粉丝

引用和评论

0 条评论
评论支持部分 Markdown 语法:**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用。你还可以使用 @ 来通知其他用户。

AltStyle によって変換されたページ (->オリジナル) /