Go 语言 gjson对Json数据进行操作

本文详细介绍了如何使用Go语言的gjson库来操作JSON数据,包括获取值、路径语法、结果类型、修饰符和路径链等功能。通过示例展示了如何解析、查询、遍历JSON对象和数组,并提供了性能基准测试。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

要开始使用 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 类型返回。numberboolnull

Resulttype 包含其中之一:

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

完整的选项列表@prettysortKeys,,,和。请参阅漂亮选项了解更多信息。indentprefixwidth

自定义修饰符

您还可以添加自定义修饰符。

例如,这里我们创建一个修饰符,使整个 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>
 

 

 

 

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

寻找优秀的自己

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值