要开始使用 GJSON,请安装 Go 并运行go get
:
$ go get -u github.com/tidwall/gjson
这将检索库。
获取一个值
Get 在 json 中搜索指定路径。路径采用点语法,例如“name.last”或“age”。当找到该值时,它会立即返回。
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()) }
这将打印:
<span style="background-color:var(--bgColor-muted, var(--color-canvas-subtle))"><span style="color:#1f2328"><span style="color:var(--fgColor-default, var(--color-fg-default))"><span style="background-color:var(--bgColor-muted, var(--color-canvas-subtle))"><code>Prichard
</code></span></span></span></span>
还有用于一次获取多个值的GetMany函数,以及用于处理 JSON 字节切片的GetBytes 。
路径语法
下面是路径语法的快速概述,有关更完整的信息,请查看GJSON 语法。
路径是一系列由点分隔的键。键可以包含特殊通配符“*”和“?”。要访问数组值,请使用索引作为键。要获取数组中的元素数或访问子路径,请使用“#”字符。点和通配符可以用“\”转义。
{ "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"]} ] }
<span style="background-color:var(--bgColor-muted, var(--color-canvas-subtle))"><span style="color:#1f2328"><span style="color:var(--fgColor-default, var(--color-fg-default))"><span style="background-color:var(--bgColor-muted, var(--color-canvas-subtle))"><code>"name.last" >> "Anderson"
"age" >> 37
"children" >> ["Sara","Alex","Jack"]
"children.#" >> 3
"children.1" >> "Alex"
"child*.2" >> "Jack"
"c?ildren.0" >> "Sara"
"fav\.movie" >> "Deer Hunter"
"friends.#.first" >> ["Dale","Roger","Jane"]
"friends.1.last" >> "Craig"
</code></span></span></span></span>
您还可以使用 查询数组中的第一个匹配项#(...)
,或使用 查找所有匹配项#(...)#
。查询支持==
,!=
,<
,<=
,>
,>=
比较运算符以及简单模式匹配%
(like)和!%
(not like)运算符。
<span style="background-color:var(--bgColor-muted, var(--color-canvas-subtle))"><span style="color:#1f2328"><span style="color:var(--fgColor-default, var(--color-fg-default))"><span style="background-color:var(--bgColor-muted, var(--color-canvas-subtle))"><code>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"
friends.#(nets.#(=="fb"))#.first >> ["Dale","Roger"]
</code></span></span></span></span>
请注意,在 v1.3.0 之前,查询使用#[...]
括号。这在 v1.3.0 中进行了更改,以避免与新的 多路径语法混淆。为了向后兼容, #[...]
将继续工作直到下一个主要版本。
结果类型
GJSON 支持 json类型string
,,,,和。数组和对象以其原始 json 类型返回。number
bool
null
Result
type 包含其中之一:
<span style="background-color:var(--bgColor-muted, var(--color-canvas-subtle))"><span style="color:#1f2328"><span style="color:var(--fgColor-default, var(--color-fg-default))"><span style="background-color:var(--bgColor-muted, var(--color-canvas-subtle))"><code>bool, for JSON booleans
float64, for JSON numbers
string, for JSON string literals
nil, for JSON null
</code></span></span></span></span>
直接访问该值:
result.Type // can be String, Number, True, False, Null, or JSON result.Str // holds the string result.Num // holds the float64 number result.Raw // holds the raw json result.Index // index of raw value in original json, zero means index unknown result.Indexes // indexes of all the elements that match on a path containing the '#' query character.
有多种方便的函数可以处理结果:
result.Exists() bool result.Value() interface{} result.Int() int64 result.Uint() uint64 result.Float() float64 result.String() string result.Bool() bool result.Time() time.Time result.Array() []gjson.Result result.Map() map[string]gjson.Result result.Get(path string) Result result.ForEach(iterator func(key, value Result) bool) result.Less(token Result, caseSensitive bool) bool
result.Value()
函数返回一个interface{}
需要类型断言的并且是以下 Go 类型之一:
boolean >> bool number >> float64 string >> string null >> nil array >> []interface{} object >> map[string]interface{}
result.Array()
函数返回一个值数组。如果结果表示不存在的值,则将返回一个空数组。如果结果不是 JSON 数组,则返回值将是包含一个结果的数组。
64 位整数
result.Int()
和result.Uint()
调用能够读取所有 64 位,从而允许使用大型 JSON 整数。
result.Int() int64 // -9223372036854775808 to 9223372036854775807 result.Uint() uint64 // 0 to 18446744073709551615
修饰符和路径链
1.2 版中的新功能是支持修饰符函数和路径链。
修饰符是对 json 执行自定义处理的路径组件。
可以使用管道字符将多个路径“链接”在一起。这对于从修改后的查询中获取结果非常有用。
@reverse
例如,在上面的 json 文档上使用内置修饰符,我们将获取children
数组并反转顺序:
<span style="background-color:var(--bgColor-muted, var(--color-canvas-subtle))"><span style="color:#1f2328"><span style="color:var(--fgColor-default, var(--color-fg-default))"><span style="background-color:var(--bgColor-muted, var(--color-canvas-subtle))"><code>"children|@reverse" >> ["Jack","Alex","Sara"]
"children|@reverse|0" >> "Jack"
</code></span></span></span></span>
目前有以下内置修饰符:
@reverse
:反转数组或对象的成员。@ugly
:从 json 文档中删除所有空格。@pretty
:使 json 文档更易于阅读。@this
:返回当前元素。它可用于检索根元素。@valid
:确保json文档有效。@flatten
:展平数组。@join
:将多个对象连接成一个对象。@keys
:返回对象的键数组。@values
:返回对象的值数组。@tostr
:将json转换为字符串。包装一个 json 字符串。@fromstr
:将 json 转换为字符串。解开 json 字符串。@group
:对对象数组进行分组。参见e4fc67c。@dig
:搜索值而不提供其完整路径。参见e8e87f2。
修饰符参数
修饰符可以接受可选参数。参数可以是有效的 JSON 文档或只是字符。
例如,@pretty
修饰符将 json 对象作为其参数。
<span style="background-color:var(--bgColor-muted, var(--color-canvas-subtle))"><span style="color:#1f2328"><span style="color:var(--fgColor-default, var(--color-fg-default))"><span style="background-color:var(--bgColor-muted, var(--color-canvas-subtle))"><code>@pretty:{"sortKeys":true}
</code></span></span></span></span>
这使得 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"} }
完整的选项列表@pretty
是sortKeys
,,,和。请参阅漂亮选项了解更多信息。indent
prefix
width
自定义修饰符
您还可以添加自定义修饰符。
例如,这里我们创建一个修饰符,使整个 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 })
<span style="background-color:var(--bgColor-muted, var(--color-canvas-subtle))"><span style="color:#1f2328"><span style="color:var(--fgColor-default, var(--color-fg-default))"><span style="background-color:var(--bgColor-muted, var(--color-canvas-subtle))"><code>"children|@case:upper" >> ["SARA","ALEX","JACK"]
"children|@case:lower|@reverse" >> ["jack","alex","sara"]
</code></span></span></span></span>
JSON 行
使用前缀支持JSON Lines..
,它将多行文档视为数组。
例如:
<span style="background-color:var(--bgColor-muted, var(--color-canvas-subtle))"><span style="color:#1f2328"><span style="color:var(--fgColor-default, var(--color-fg-default))"><span style="background-color:var(--bgColor-muted, var(--color-canvas-subtle))"><code>{"name": "Gilbert", "age": 61}
{"name": "Alexa", "age": 34}
{"name": "May", "age": 57}
{"name": "Deloise", "age": 44}
</code></span></span></span></span>
<span style="background-color:var(--bgColor-muted, var(--color-canvas-subtle))"><span style="color:#1f2328"><span style="color:var(--fgColor-default, var(--color-fg-default))"><span style="background-color:var(--bgColor-muted, var(--color-canvas-subtle))"><code>..# >> 4
..1 >> {"name": "Alexa", "age": 34}
..3 >> {"name": "Deloise", "age": 44}
..#.name >> ["Gilbert","Alexa","May","Deloise"]
..#(name="May").age >> 57
</code></span></span></span></span>
ForEachLines
函数将遍历 JSON 行。
gjson.ForEachLine(json, func(line gjson.Result) bool{ println(line.String()) return true })
获取嵌套数组值
假设您想要以下 json 中的所有姓氏:
{ "programmers": [ { "firstName": "Janet", "lastName": "McLaughlin", }, { "firstName": "Elliotte", "lastName": "Hunter", }, { "firstName": "Jason", "lastName": "Harold", } ] }
您可以像这样使用路径“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
将停止迭代。
result := gjson.Get(json, "programmers") result.ForEach(func(key, value gjson.Result) bool { println(value.String()) return true // keep iterating })
简单解析并获取
有一个Parse(json)
函数可以执行简单的解析,然后result.Get(path)
搜索结果。
例如,所有这些都会返回相同的结果:
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 step if gjson.Get(json, "name.last").Exists() { println("has a last name") }
验证 JSON
Get*
和Parse*
functions 期望 json 格式良好。错误的 json 不会恐慌,但可能会返回意外的结果。
如果您从不可预测的来源使用 JSON,那么您可能需要在使用 GJSON 之前进行验证。
if !gjson.Valid(json) { return errors.New("invalid json") } value := gjson.Get(json, "name.last")
解组到地图
解组为map[string]interface{}
:
m, ok := gjson.Parse(json).Value().(map[string]interface{}) if !ok { // not a map }
使用字节
如果您的 JSON 包含在[]byte
切片中,则可以使用GetBytes函数。这优于Get(string(data), path)
。
var json []byte = ... result := gjson.GetBytes(json, path)
如果您正在使用该gjson.GetBytes(json, path)
函数并且希望避免转换result.Raw
为 a []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) }
这是原始 json 的尽力而为的无分配子切片。该方法利用了result.Index
字段,该字段是原始数据在原始 json 中的位置。的值有可能result.Index
等于零,在这种情况下,result.Raw
被转换为 a []byte
。
表现
GJSON 的基准以及encoding/json, ffjson, EasyJSON, jsonparser,和json-iterator
<span style="background-color:var(--bgColor-muted, var(--color-canvas-subtle))"><span style="color:#1f2328"><span style="color:var(--fgColor-default, var(--color-fg-default))"><span style="background-color:var(--bgColor-muted, var(--color-canvas-subtle))"><code>BenchmarkGJSONGet-16 11644512 311 ns/op 0 B/op 0 allocs/op
BenchmarkGJSONUnmarshalMap-16 1122678 3094 ns/op 1920 B/op 26 allocs/op
BenchmarkJSONUnmarshalMap-16 516681 6810 ns/op 2944 B/op 69 allocs/op
BenchmarkJSONUnmarshalStruct-16 697053 5400 ns/op 928 B/op 13 allocs/op
BenchmarkJSONDecoder-16 330450 10217 ns/op 3845 B/op 160 allocs/op
BenchmarkFFJSONLexer-16 1424979 2585 ns/op 880 B/op 8 allocs/op
BenchmarkEasyJSONLexer-16 3000000 729 ns/op 501 B/op 5 allocs/op
BenchmarkJSONParserGet-16 3000000 366 ns/op 21 B/op 0 allocs/op
BenchmarkJSONIterator-16 3000000 869 ns/op 693 B/op 14 allocs/op
</code></span></span></span></span>
使用的 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;" } } }
每个操作都通过以下搜索路径之一轮换:
<span style="background-color:var(--bgColor-muted, var(--color-canvas-subtle))"><span style="color:#1f2328"><span style="color:var(--fgColor-default, var(--color-fg-default))"><span style="background-color:var(--bgColor-muted, var(--color-canvas-subtle))"><code>widget.window.name
widget.image.hOffset
widget.text.onMouseUp
</code></span></span></span></span>