
























gjson 是 Go 语言中一款非常轻量且高性能的 JSON 解析库,被很多开发者称为处理 JSON 的“瑞士军刀”。它最大的特点是无需定义结构体,也不用进行繁琐的类型断言,仅用一行代码就能提取任意深度嵌套的 JSON 值。
下面为你详细介绍它的安装和核心用法:
在你的项目目录下执行以下命令即可安装:
go get -u github.com/tidwall/gjson
gjson 的核心在于其强大的路径语法。你只需要传入 JSON 字符串和对应的键路径(使用 . 分隔),就能快速获取数据。
假设我们有如下的 JSON 数据:
const json = `{
"user": {
"name": "小明",
"age": 18
},
"tags": ["Go", "Rust", "Python"],
"friends": [
{"name": "张三", "age": 20},
{"name": "李四", "age": 26}
]
}`
直接使用 gjson.Get 配合点号 . 即可获取深层嵌套的值;访问数组元素时,直接使用索引(从0开始)。
import (
"fmt"
"github.com/tidwall/gjson"
)
func main() {
name := gjson.Get(json, "user.name").String() // 获取嵌套字段: "小明"
age := gjson.Get(json, "user.age").Int() // 获取数字: 18
firstTag := gjson.Get(json, "tags.0").String() // 获取数组第一个元素: "Go"
fmt.Println(name, age, firstTag)
}
使用 # 符号可以获取数组长度,或者使用 .# 来批量提取数组中所有对象的某个字段。
// 获取 tags 数组的长度
tagCount := gjson.Get(json, "tags.#").Int() // 结果: 3
// 批量提取 friends 数组中所有人的名字
names := gjson.Get(json, "friends.#.name").Array()
for _, n := range names {
fmt.Println(n.String()) // 依次输出: 张三, 李四
}
gjson 支持类似 SQL 的条件查询,语法为 #(条件),可以精准筛选出符合要求的元素。
// 查找 friends 数组中第一个年龄大于 25 岁的人的名字
result := gjson.Get(json, `friends.#(age>25).name`)
if result.Exists() {
fmt.Println("找到啦:", result.String()) // 输出: 李四
}
gjson.Get 返回的是一个 gjson.Result 对象,你可以根据需要将其转换为对应的 Go 原生类型。
.String()、.Int()、.Float()、.Bool()、.Array()(转为 []gjson.Result 切片)、.Map()(转为 map[string]gjson.Result)。.Exists() 方法进行判断,这比判断返回值是否为空字符串更可靠。// 推荐的健壮写法
if val := gjson.Get(json, "user.email"); val.Exists() {
fmt.Println(val.String())
} else {
fmt.Println("该字段不存在")
}
Get 操作仅需约 200ns,且实现了零内存分配,非常适合高并发场景。如果你需要对同一个 JSON 字符串进行多次查询,可以先使用 parsed := gjson.Parse(json) 预解析,再通过 parsed.Get("path") 提取,效率会更高。[]byte 类型(例如从 HTTP 请求的 Body 中直接读取),请使用 gjson.GetBytes(data, "path"),这样可以避免不必要的 string 类型转换开销。gjson.Valid(json) 检查其格式是否合法。sjson 库一起使用。一个完整的 Go 示例代码。
这段代码模拟了我们在实际开发中经常遇到的场景:处理一个包含用户信息、标签、朋友列表的复杂嵌套 JSON。它把前面提到的基础取值、数组操作、条件过滤、类型安全判断以及性能优化等核心用法全部串联了起来。
直接复制以下代码到本地环境运行:
package main
import (
"fmt"
"github.com/tidwall/gjson"
)
func main() {
// 模拟一份复杂的嵌套 JSON 数据
jsonData := `{
"user": {
"name": "小明",
"age": 18,
"is_active": true
},
"tags": ["Go", "Rust", "Python"],
"friends": [
{"name": "张三", "age": 20, "city": "北京"},
{"name": "李四", "age": 26, "city": "上海"},
{"name": "王五", "age": 30, "city": "深圳"}
],
"metadata": {
"fav.movie": "星际穿越"
}
}`
fmt.Println("=== 1. 基础取值与嵌套访问 ===")
name := gjson.Get(jsonData, "user.name").String()
age := gjson.Get(jsonData, "user.age").Int()
isActive := gjson.Get(jsonData, "user.is_active").Bool()
// 键名中包含特殊字符(如点号),需要使用 \ 进行转义
favMovie := gjson.Get(jsonData, `metadata.fav\.movie`).String()
fmt.Printf("用户名: %s, 年龄: %d, 是否活跃: %v, 喜欢的电影: %s\n\n", name, age, isActive, favMovie)[[source_group_web_1]]
fmt.Println("=== 2. 数组遍历与批量提取 ===")
// 获取数组长度
tagCount := gjson.Get(jsonData, "tags.#").Int()
fmt.Printf("用户拥有的标签数量: %d\n", tagCount)
// 批量提取 friends 数组中所有人的名字
fmt.Print("所有朋友的名字: ")
names := gjson.Get(jsonData, "friends.#.name").Array()
for _, n := range names {
fmt.Printf("%s ", n.String())
}
fmt.Println("\n")
fmt.Println("=== 3. 条件过滤查询 ===")
// 查找 friends 数组中第一个年龄大于 25 岁的人的名字
result := gjson.Get(jsonData, `friends.#(age>25).name`)
if result.Exists() {
fmt.Printf("找到第一位年龄超过25岁的朋友: %s\n", result.String())
}
// 找出所有城市在上海或北京的朋友对象
fmt.Println("筛选出住在一线城市(北京/上海)的朋友:")
gjson.Get(jsonData, `friends.#(city%"*京|*海")#`).ForEach(func(_, value gjson.Result) bool {
friendName := value.Get("name").String()
friendCity := value.Get("city").String()
fmt.Printf("- %s 在 %s\n", friendName, friendCity)
return true // 返回 true 继续遍历,返回 false 则停止
})
fmt.Println()[[source_group_web_2]]
fmt.Println("=== 4. 存在性判断与健壮写法 ===")
// 尝试获取一个不存在的字段
emailResult := gjson.Get(jsonData, "user.email")
if emailResult.Exists() {
fmt.Println("用户的邮箱是:", emailResult.String())
} else {
fmt.Println("警告: user.email 字段不存在!")
}
fmt.Println()
fmt.Println("=== 5. 性能优化技巧 ===")
// 如果需要对同一个 JSON 字符串进行多次高频查询,建议先预解析
parsedJSON := gjson.Parse(jsonData)
fmt.Println("使用预解析后的对象获取名字:", parsedJSON.Get("user.name").String())
}
当你执行这段代码时,终端会打印出如下内容:
=== 1. 基础取值与嵌套访问 ===
用户名: 小明, 年龄: 18, 是否活跃: true, 喜欢的电影: 星际穿越
=== 2. 数组遍历与批量提取 ===
用户拥有的标签数量: 3
所有朋友的名字: 张三 李四 王五
=== 3. 条件过滤查询 ===
找到第一位年龄超过25岁的朋友: 李四
筛选出住在一线城市(北京/上海)的朋友:
- 张三 在 北京
- 李四 在 上海
=== 4. 存在性判断与健壮写法 ===
警告: user.email 字段不存在!
=== 5. 性能优化技巧 ===
使用预解析后的对象获取名字: 小明
.ForEach() 方法。它的回调函数需要返回一个 bool 值,如果你在处理过程中找到了想要的结果想提前退出循环,直接 return false 即可。metadata.fav\.movie,因为真实的键名里带有一个点(.),而 gjson 默认用点来代表层级,所以必须加反斜杠 \ 告诉它“这个点是键名的一部分”。Result 对象支持链式调用,比如在遍历朋友时,可以直接在 value 上继续 .Get("name"),非常丝滑。此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。