GJSON路径是一种文本字符串语法,它描述了从JSON有效载荷中快速检索值的搜索模式。
GJSON路径易于表示为一系列由字符分隔的组件。
除了字符之外,还有一些字符具有特殊含义: , 、 . 、 | 、 # 、 @ 、 \ 、 * 、 ! 、 ?
,
.
|
#
@
\
*
!
?
当json的key里包含特殊字符且需要根据key进行提取时,需要进行转义操作
安装命令如下
1
go get -u github.com/tidwall/gjson
github的地址为 https://github.com/tidwall/gjson
语法介绍可见 https://github.com/tidwall/gjson/blob/master/SYNTAX.md
该语法可以在 GJSON Playground https://gjson.dev/ 上在线运行查看结果
使用的json示例如下:
1234567891011
{ "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"]} ]}
12345678910
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
在许多情况下,您只需要按对象名称或数组索引检索值。
对于实例json,以下语法对应的结果如下:
name.last "Anderson"name.first "Tom"age 37children ["Sara","Alex","Jack"]children.0 "Sara"children.1 "Alex"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"]}]friends.1 {"first": "Roger", "last": "Craig", "age": 68}friends.1.first "Roger"friends.#.first ["Dale","Roger","Jane"]friends.*.first 无结果
GJSON支持json类型 string 、 number 、 bool 和 null 。数组和对象作为其原始json类型返回。
string
number
bool
null
Result 类型包含以下内容之一:
Result
1234
bool, for JSON booleansfloat64, for JSON numbersstring, for JSON string literalsnil, for JSON null
要直接访问值:
123456
result.Type // can be String, Number, True, False, Null, or JSONresult.Str // holds the stringresult.Num // holds the float64 numberresult.Raw // holds the raw jsonresult.Index // index of raw value in original json, zero means index unknownresult.Indexes // indexes of all the elements that match on a path containing the '#' query character.
有多种方便的函数可以处理结果:
12345678910111213
result.Exists() boolresult.Value() interface{}result.Int() int64result.Uint() uint64result.Float() float64result.String() stringresult.Bool() boolresult.Time() time.Timeresult.Array() []gjson.Resultresult.Map() map[string]gjson.Resultresult.Get(path string) Resultresult.ForEach(iterator func(key, value Result) bool)result.Less(token Result, caseSensitive bool) bool
result.Value() 函数返回的类型是 interface{} ,它是以下Go类型之一:
result.Value()
interface{}
boolean >> boolnumber >> float64string >> stringnull >> nilarray >> []interface{}object >> map[string]interface{}
result.Array() 函数返回一个值数组。如果结果表示一个不存在的值,那么将返回一个空数组。如果结果不是JSON数组,则返回值将是包含一个结果的数组。
result.Array()
result.Int() 和 result.Uint() 调用能够读取所有64位,允许使用大型JSON整数。
result.Int()
result.Uint()
12
result.Int() int64 // -9223372036854775808 to 9223372036854775807result.Uint() uint64 // 0 to 18446744073709551615
可以用 Parse(json) 函数执行简单的解析, result.Get(path) 函数搜索结果。
Parse(json)
result.Get(path)
例如,所有这些都将返回相同的结果:
123
gjson.Parse(json).Get("name").Get("last")gjson.Get(json, "name").Get("last")gjson.Get(json, "name.last")
有时你只想知道一个值是否存在。
value := gjson.Get(json, "name.last")if !value.Exists() { println("no last name")} else { println(value.String())}// Or as one stepif gjson.Get(json, "name.last").Exists() { println("has a last name")}
Get* 和 Parse* 函数的使用前提是一个正常的json,异常的json数据不会引起 panic ,但是会返回一个意想不到的结果。
Get*
Parse*
panic
如果您使用的是来自不可预测的源的JSON,那么您可能需要在使用GJSON之前进行验证。
if !gjson.Valid(json) { return errors.New("invalid json")}value := gjson.Get(json, "name.last")
将json转换为 map[string]interface{} 的结构
map[string]interface{}
m, ok := gjson.Parse(json).Value().(map[string]interface{})if !ok { // not a map}
如果JSON包含在 []byte 切片中,则有 GetBytes 函数。这优于 Get(string(data), path)
[]byte
GetBytes
Get(string(data), path)
var json []byte = ...result := gjson.GetBytes(json, path)
如果您正在使用 gjson.GetBytes(json,path) 函数,并且希望避免将 result.Raw 转换为 []byte ,则可以使用以下模式:
gjson.GetBytes(json,path)
result.Raw
12345678
var json []byte = ...result := gjson.GetBytes(json, path)var raw []byteif result.Index > 0 { raw = json[result.Index:result.Index+len(result.Raw)]} else { raw = []byte(result.Raw)}
这是原始json的无分配子片。此方法使用 result.Index 字段,该字段是原始json中原始数据的位置。 result.Index 的值可能等于零,在这种情况下 result.Raw 被转换为 []byte 。
result.Index
GetMany 函数可用于同时获取多个值。
GetMany
results := gjson.GetMany(json, "name.first", "name.last", "age")
返回值是一个 []Result ,它将始终包含与输入路径完全相同数量的项。
[]Result
key可以包含特殊通配符和 . 、 * 和 ?
child*.2 "Jack"c?ildren.0 "Sara"
特殊字符需要转义才能使用
fav\.movie "Deer Hunter"
在源代码中硬编码路径时,还需要确保字符正确转义
// Goval := gjson.Get(json, "fav\\.movie") // must escape the slashval := gjson.Get(json, `fav\.movie`) // no need to escape the slash
// Rustlet val = gjson::get(json, "fav\\.movie") // must escape the slashlet val = gjson::get(json, r#"fav\.movie"#) // no need to escape the slash
# 字符允许深入JSON数组,也可以使用 # 获取数组的长度
friends.# 3friends.#.age [44,68,47]
对于以下json
1234567891011121314
{ "programmers": [ { "firstName": "Janet", "lastName": "McLaughlin", }, { "firstName": "Elliotte", "lastName": "Hunter", }, { "firstName": "Jason", "lastName": "Harold", } ]}
可以使用 programmers.#.lastName 查询
programmers.#.lastName
result := gjson.Get(json, "programmers.#.lastName")for _, name := range result.Array() { println(name.String())}
还可以查询数组中的对象:
name := gjson.Get(json, `programmers.#(lastName="Hunter").firstName`)println(name.String()) // prints "Elliotte"
ForEach 函数允许快速遍历对象或数组。键和值被传递给对象的迭代器函数。仅为数组传递值。从迭代器返回 false 将停止迭代。
ForEach
false
12345
result := gjson.Get(json, "programmers")result.ForEach(func(key, value gjson.Result) bool { println(value.String()) return true // keep iterating})
可以使用 #(...) 查询数组的第一个符合条件的结果或者使用 #(...)# 查询全部符合条件的结果。查询语法支持 == , != , < , <= , > , >= 比较运算符,简单的模式匹配支持 % (like)和 !% (not like)
#(...)
#(...)#
==
!=
<
<=
>
>=
%
!%
friends.#(last=="Murphy").first "Dale"friends.#(last=="Murphy")#.first ["Dale","Jane"]friends.#(age>45)#.last ["Craig","Murphy"]friends.#(first%"D*").last "Murphy"friends.#(first!%"D*").last "Craig"
要查询数组中的非对象值,可以放弃运算符右侧的字符串。
children.#(!%"*a*") "Alex"children.#(%"*a*")# ["Sara","Jack"]
friends.#(nets.#(=="fb"))#.first >> ["Dale","Roger"]
请注意,在v1.3.0之前,查询使用 #[…] 括号。为了避免与新的多路径语法混淆,v1.3.0中对此进行了更改。为了向后兼容, #[…] 将继续工作直到下一个主要版本。
#[…]
~ (波浪号)运算符将在比较之前将值转换为布尔值。
~
l例如对下面的json
123456789101112131415
{ "vals": [ { "a": 1, "b": true }, { "a": 2, "b": true }, { "a": 3, "b": false }, { "a": 4, "b": "0" }, { "a": 5, "b": 0 }, { "a": 6, "b": "1" }, { "a": 7, "b": 1 }, { "a": 8, "b": "true" }, { "a": 9, "b": false }, { "a": 10, "b": null }, { "a": 11 } ]}
可以查询所有true(ish)或false(ish)值:
vals.#(b==~true)#.a >> [1,2,6,7,8]vals.#(b==~false)#.a >> [3,4,5,9,10,11]
最后一个不存在的值被视为false
使用选择器语法将多个路径连接到新的JSON文档中。
{name.first,age,"murphys":friends.#(last="Murphy")#.first}[name.first,age,children.0]
. 是标准分隔符,但也可以使用 | ,在大多数情况下,它们都会返回相同的结果.
主要注意的是,在使用 # 查询数组时, | 与 . 的用法是不同的
friends.0.first "Dale"friends|0.first "Dale"friends.0|first "Dale"friends|0|first "Dale"friends|# 3friends.# 3friends.#(last="Murphy")# [{"first": "Dale", "last": "Murphy", "age": 44},{"first": "Jane", "last": "Murphy", "age": 47}]friends.#(last="Murphy")#.first ["Dale","Jane"]friends.#(last="Murphy")#|first <non-existent>friends.#(last="Murphy")#.0 []friends.#(last="Murphy")#|0 {"first": "Dale", "last": "Murphy", "age": 44}friends.#(last="Murphy")#.# []friends.#(last="Murphy")#|# 2
分析如下如下:
friends.#(last="Murphy")# 获取到的结果为
friends.#(last="Murphy")#
[{"first": "Dale", "last": "Murphy", "age": 44},{"first": "Jane", "last": "Murphy", "age": 47}]
.first 后缀会在每个数组元素返回之前处理它的 first 路径,所以最终结果为
.first
first
["Dale","Jane"]
但 |first 后缀实际上处理前一个结果之后的第一个路径。由于前面的结果是一个数组,而不是一个对象,因此无法处理,因为first不存在。
|first
然而,|0后缀返回
{"first": "Dale", "last": "Murphy", "age": 44}
因为0是上一个结果的第一个索引。
修饰符是对JSON执行自定义处理的路径组件。
例如,在上面的JSON负载上使用内置的 @reverse 修饰符将反转子数组:
@reverse
children.@reverse ["Jack","Alex","Sara"][email protected] "Jack"
内置修饰符的含义如下:
@ugly
@pretty
@this
@valid
@flatten
@join
@keys
@values
@tostr
@fromstr
@group
修饰符可以接受可选参数。参数可以是有效的JSON负载,也可以是字符。
例如,修饰符将json对象作为 @pretty 的参数
@pretty:{"sortKeys":true}
这使得json很漂亮,并对其所有键进行排序。
{ "age":37, "children": ["Sara","Alex","Jack"], "fav.movie": "Deer Hunter", "friends": [ {"age": 44, "first": "Dale", "last": "Murphy"}, {"age": 68, "first": "Roger", "last": "Craig"}, {"age": 47, "first": "Jane", "last": "Murphy"} ], "name": {"first": "Tom", "last": "Anderson"}}
children.@reverse >> ["Jack","Alex","Sara"][email protected] >> "Jack"{name.first,"murphys":friends.0}.@pretty >> beautiful JSON{name.first,"murphys":friends.0}.@ugly >> compact JSON[@this].#(age>35).name.last >> "Anderson"
可以添加自定义修改器。
例如这里我们创建了一个修饰符,它使整个json文档变为大写或小写。
gjson.AddModifier("case", func(json, arg string) string { if arg == "upper" { return strings.ToUpper(json) } if arg == "lower" { return strings.ToLower(json) } return json})"children.@case:upper" ["SARA","ALEX","JACK"]"children.@case:lower.@reverse" ["jack","alex","sara"]
自定义修改器在Rust版本中尚不可用
从v1.3.0开始,GJSON增加了将多个路径连接在一起以形成新文档的功能。在或之间包装逗号分隔的路径将分别产生一个新的数组或对象
{name.first,age,"the_murphys":friends.#(last="Murphy")#.first}
在这里,我们为姓为“Murphy”的朋友选择了名字、年龄和名字。
您将注意到,可以提供一个可选的键,在本例中为“the murphys”,以强制将键分配给一个值。否则,将使用实际字段的名称,在本例中为“first”。如果无法确定名称,则使用“ ”。
运行结果如下:
{"first":"Tom","age":37,"the_murphys":["Dale","Jane"]}
从v1.12.0开始,GJSON增加了对json文本的支持,这提供了一种构造json静态块的方法。这在使用多路径构建新的json文档时尤其有用。
json文本以“!”开头声明字符。
例如,使用给定的多路径:
{name.first,age,"company":!"Happysoft","employed":!true}
这里我们选择了名字和年龄。然后添加两个新字段,“company”和“employed”。
运行结果为
{"first":"Tom","age":37,"company":"Happysoft","employed":true}
支持使用 .. 前缀,将多行文档视为数组。
..
对于以下文本
{"name": "Gilbert", "age": 61}{"name": "Alexa", "age": 34}{"name": "May", "age": 57}{"name": "Deloise", "age": 44}
对应的结果为
..# >> 4..1 >> {"name": "Alexa", "age": 34}..3 >> {"name": "Deloise", "age": 44}..#.name >> ["Gilbert","Alexa","May","Deloise"]..#(name="May").age >> 57
ForEachLines 函数将遍历JSON行。
ForEachLines
gjson.ForEachLine(json, func(line gjson.Result) bool{ println(line.String()) return true})