超好用Golang解析JSON包GJSON
基本概述
相信使用过动态语言的人,都觉得解析JSON是很简单的,只需要简单的几行代码就可以拿到解析好的JSON对象。例如Python解析JSON如下所示
result = {"name": "Bob", "age": 18}print(result["name"]) // "Bob"
而Golang语言中简单的数据结构可使用map[string]interface{},但如果JSON嵌套格式太复杂,用这种方式特别容易绕晕,而如果预先定义struct结构体,在用json.Unmarshal把数据解析到结构体中,取出对应的数据,代码量编写会增加许多。如下所示
type Student struct { Name string `json:"name"`
Age int `json:"age"`
}
jsonData := []byte(`
{
"name": "Bob",
"age": 18
}`)
var student Student
err := json.Unmarshal(jsonData, &student)
if err != nil {
fmt.Println(err)
}
fmt.Println(student.Name)
经过对比,发现GoLang解析JSON有没有相对简单的方法呢,作为受众这么广的语言,肯定是有解决方法的,这里就介绍一下今天要隆重介绍的第三方包GJSON。
GJSON是一个Go包,它提供了一种快速,简单的方法来从JSON文档中获取值。它具有诸如单行检索,点符号路径,迭代和解析JSON行之类的功能。项目地址: https://github.com/tidwall/gjson
GJSON 获得JSON的值,比较简单,操作如下
package mainimport "github.com/tidwall/gjson"
const json = `{"name":{"first":"Janet","last":"Prichard"},"age":47}`
func main() {
value := gjson.Get(json, "name.last")
println(value.String()) // Prichard
}
经过对比,发现GJSON获得值的方式,相对简单许多。GJSON还支持简单的路径语法去获得值,具体使用方式下面会详细介绍
安装
要使用GSON, 首先需要安装Go然后执行如下go get命令
$ go get -u github.com/tidwall/gjson
使用方式
一、路径语法
以下是路径语法的快速概述,有关更多完整信息,请查看GJSON语法。路径是一系列由点分隔的键。==键可能包含特殊的通配符"*"和"?"。要访问数组值,请使用索引作为键。要获取数组中的元素数或访问子路径,请使用“#”字符。点和通配符可以用""进行转义。==
package mainimport (
"fmt"
"github.com/tidwall/gjson"
)
const json = `
{
"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"]}
]
}
`
func main() {
// 得到一个值
value1 := gjson.Get(json, "age")
fmt.Println(value1) // 37
value2 := gjson.Get(json, "name.last")
fmt.Println(value2) // Anderson
value3 := gjson.Get(json, "name.first")
fmt.Println(value3) // Tom
value4 := gjson.Get(json, "children")
fmt.Println(value4) // ["Sara","Alex","Jack"]
// children数组的总数(元素个数)
value5 := gjson.Get(json, "children.#")
fmt.Println(value5) // 3
// children数组的第二个元素
value6 := gjson.Get(json, "children.1")
fmt.Println(value6) // Alex
// *表示任意个字符(包括0个)
value7 := gjson.Get(json, "child*.2")
fmt.Println(value7) // Jack
// ?表示0个或1个字符
value8 := gjson.Get(json, "c?ildren.0")
fmt.Println(value8) // Sara
// 表示转义
value9 := gjson.Get(json, "fav\.movie")
fmt.Println(value9) // Deer Hunter
value10 := gjson.Get(json, "friends.#.first")
fmt.Println(value10) // ["Dale","Roger","Jane"]
value11 := gjson.Get(json, "friends.1.last")
fmt.Println(value11) // Craig
/*
也可以使用#(...)查询数组中的第一个匹配项,或使用#(...)# 查找所有匹配项。
查询支持==,!=,<,<=,>,>= 比较运算符,以及支持%(类似)和!%(不类似)的简单模式匹配。
*/
// 查找数组中的第一个匹配项
value12 := gjson.Get(json, `friends.#(last=="Murphy").first`)
fmt.Println(value12) // Dale
// 查找数组中的所有匹配项
value13 := gjson.Get(json, `friends.#(last=="Murphy")#.first`)
fmt.Println(value13) // ["Dale","Jane"]
// 查找friends数组中所有age大于45的last值
value14 := gjson.Get(json, `friends.#(age>45)#.last`)
fmt.Println(value14) // ["Craig","Murphy"]
// 查找friends数组中第一个first对应的值是以D开头的 last值
value15 := gjson.Get(json, `friends.#(first%"D*").last`)
fmt.Println(value15) // Murphy
// 查找friends数组中第一个first对应的值是不以D开头的 last值
value16 := gjson.Get(json, `friends.#(first!%"D*").last`)
fmt.Println(value16) // Craig
// 查找friends数组中nets数组值包含fb
value17 := gjson.Get(json, `friends.#(nets.#(=="fb"))#.first`)
fmt.Println(value17) // ["Dale","Roger"]
}
二、获取嵌套数组值
package mainimport (
"fmt"
"github.com/tidwall/gjson"
)
const json = `
{
"programmers": [
{
"firstName": "Janet",
"lastName": "McLaughlin",
}, {
"firstName": "Elliotte",
"lastName": "Hunter",
}, {
"firstName": "Jason",
"lastName": "Harold",
}
]
}
`
func main(){
// 获取嵌套数组值
result := gjson.Get(json, "programmers.#.lastName")
for _, name := range result.Array() {
println(name.String()) // prints McLaughlin Hunter Harold
}
// 查询数组中的对象
name := gjson.Get(json, `programmers.#(lastName="Hunter").firstName`)
println(name.String()) // prints "Elliotte"
// 遍历对象或数组
result.ForEach(func(key, value gjson.Result) bool {
println(value.String())
return true // keep iterating
})
三、简单的Parse和Get使用
以下三种写法会得到相同的结果
package mainimport (
"fmt"
"github.com/tidwall/gjson"
)
const json = `{"name":{"first":"Janet","last":"Prichard"},"age":47}`
func main(){
value1 := gjson.Parse(json).Get("name").Get("last")
fmt.Println(value1) // Prichard
value2 := gjson.Get(json, "name").Get("last")
fmt.Println(value2) // Prichard
value3 := gjson.Get(json, "name.last")
fmt.Println(value3) // Prichard
// 检测值是否已经存在
value := gjson.Get(json, "name.last")
if !value.Exists() {
println("no last name")
} else {
println(value.String()) // Prichard
}
// Or as one step
if gjson.Get(json, "name.last").Exists() {
println("has a last name") // has a last name
}
// 验证是否为json
if !gjson.Valid(json) {
fmt.Println("invalid json")
return
}
value4 := gjson.Get(json, "name.last")
fmt.Println(value4)
// 将json字符串解码到map中
m, ok := gjson.Parse(json).Value().(map[string]interface{})
if !ok {
// not a map
fmt.Println("not map")
}
fmt.Println(m) // map[programmers:[map[firstName:Janet lastName:McLaughlin] map[firstName:Elliotte lastName:Hunter] map[firstName:Jason lastName:Harold]]]
// 一次获取多个值
results := gjson.GetMany(json, "name.first", "name.last", "age")
fmt.Println(results) // [Janet Prichard 47]
}
四、以字节方式工作
如果您的JSON包含在[]byte切片中,则存在GetBytes函数。这比Get(string(data),path)更好
var json []byte = ...result := gjson.GetBytes(json, path)
如果您使用的是gjson.GetBytes(json,path)函数,并且想要避免将result.Raw转换为[]byte,则可以使用以下模式:
var json []byte = ...result := gjson.GetBytes(json, path)
var raw []byte
if result.Index > 0 {
raw = json[result.Index:result.Index+len(result.Raw)]
} else {
raw = []byte(result.Raw)
}
性能
GJSON 和encoding/json,ffjson,EasyJSON, jsonparser和json-iterator 的性能测试
BenchmarkGJSONGet-8 3000000 372 ns/op 0 B/op 0 allocs/opBenchmarkGJSONUnmarshalMap-8 900000 4154 ns/op 1920 B/op 26 allocs/op
BenchmarkJSONUnmarshalMap-8 600000 9019 ns/op 3048 B/op 69 allocs/op
BenchmarkJSONDecoder-8 300000 14120 ns/op 4224 B/op 184 allocs/op
BenchmarkFFJSONLexer-8 1500000 3111 ns/op 896 B/op 8 allocs/op
BenchmarkEasyJSONLexer-8 3000000 887 ns/op 613 B/op 6 allocs/op
BenchmarkJSONParserGet-8 3000000 499 ns/op 21 B/op 0 allocs/op
BenchmarkJSONIterator-8 3000000 812 ns/op 544 B/op 9 allocs/op
JSON 数据使用如下
{ "widget": {
"debug": "on",
"window": {
"title": "Sample Konfabulator Widget",
"name": "main_window",
"width": 500,
"height": 500
},
"image": {
"src": "Images/Sun.png",
"hOffset": 250,
"vOffset": 250,
"alignment": "center"
},
"text": {
"data": "Click Here",
"size": 36,
"style": "bold",
"vOffset": 100,
"alignment": "center",
"onMouseUp": "sun1.opacity = (sun1.opacity / 100) * 90;"
}
}
}
每个操作都通过以下搜索路径之一进行轮换:
widget.window.namewidget.image.hOffset
widget.text.onMouseUp
这些基准测试是基于MacBook Pro 15" 2.8 GHz Intel Core i7 和Go 1.8版本
本文翻译自GJSON的Readme文档,每个Demo都经过测试,有疑问可以反馈联系
以上是 超好用Golang解析JSON包GJSON 的全部内容, 来源链接: utcz.com/z/512100.html