GolangJSON解析包GJSON详解

编程

基本概述

相信使用过动态语言的人,都觉得解析JSON是很简单的,只需要简单的几行代码就可以拿到解析好的JSON对象。例如Python解析JSON如下所示

import json

jsonStr = "{"name": "Bob", "age": 18}"

result = json.loads(jsonStr)

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 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()) // Prichard

}

经过对比,发现GJSON获得值的方式,相对简单许多。GJSON还支持简单的路径语法去获得值,具体使用方式下面会详细介绍

安装

要使用GSON, 首先需要安装Go然后执行如下go get命令

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

使用方式

一、路径语法

以下是路径语法的快速概述,有关更多完整信息,请查看GJSON语法。路径是一系列由点分隔的键。==键可能包含特殊的通配符"*"和"?"。要访问数组值,请使用索引作为键。要获取数组中的元素数或访问子路径,请使用“#”字符。点和通配符可以用""进行转义。==

package main

import (

"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 main

import (

"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 main

import (

"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/op

BenchmarkGJSONUnmarshalMap-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.name

widget.image.hOffset

widget.text.onMouseUp

这些基准测试是基于MacBook Pro 15" 2.8 GHz Intel Core i7 和Go 1.8版本

本文翻译自GJSON的Readme文档,每个Demo都经过测试,有疑问可以反馈联系,另外GJSON通过对路径进行匹配,有兴趣的可以看一下JSONPATH相关文章:https://goessner.net/articles/JsonPath/

以上是 GolangJSON解析包GJSON详解 的全部内容, 来源链接: utcz.com/z/512098.html

回到顶部