给大家分享一个高性能的 Go JSON 解析库,特别适合处理动态或不确定结构的 JSON 数据。
1. gjson介绍
1.1 高频使用场景
-
处理动态 JSON API(如第三方 API)
-
日志分析工具
-
配置解析
-
需要高性能 JSON 提取的场景
1.2 GitHub 数据
-
⭐ Stars: 13k+
-
被很多知名项目使用
-
社区活跃度高
1.3 适用项目类型
-
微服务网关
-
数据管道工具
-
监控和日志系统
-
需要快速原型开发的项目
1.4 gjson的安装
1
|
go get -u github.com/tidwall/gjson
|
2. 基本用法
2.1 简单字段提取
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
package main
import (
"fmt"
"github.com/tidwall/gjson"
)
func main() {
json := `{
"name": "张三",
"age": 25,
"email": "zhangsan@example.com"
}`
// 提取单个字段
name := gjson.Get(json, "name")
fmt.Println(name.String()) // 张三
age := gjson.Get(json, "age")
fmt.Println(age.Int()) // 25
fmt.Println(age.Type) // Number
}
|
3. 常用数据类型提取
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
|
json := `{
"user": {
"id": 123,
"name": "李四",
"active": true,
"score": 95.5,
"tags": ["go", "backend", "dev"],
"profile": {
"birthday": "1990-01-01",
"address": null
}
}
}`
// 字符串
name := gjson.Get(json, "user.name").String()
fmt.Println(name) // 李四
// 整数
id := gjson.Get(json, "user.id").Int()
fmt.Println(id) // 123
// 布尔值
active := gjson.Get(json, "user.active").Bool()
fmt.Println(active) // true
// 浮点数
score := gjson.Get(json, "user.score").Float()
fmt.Println(score) // 95.5
// 判断是否存在
if gjson.Get(json, "user.profile.address").Exists() {
fmt.Println("地址字段存在")
} else {
fmt.Println("地址字段不存在或为null")
}
|
4. 路径语法(强大功能)
4.1 嵌套访问
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
json := `{
"store": {
"book": [
{
"title": "Go语言编程",
"price": 59.9,
"author": "张三"
},
{
"title": "Redis实战",
"price": 49.9,
"author": "李四"
}
]
}
}`
// 访问嵌套字段
title := gjson.Get(json, "store.book.0.title").String()
fmt.Println(title) // Go语言编程
// 使用点号路径
author := gjson.Get(json, "store.book.1.author").String()
fmt.Println(author) // 李四
|
4.2 数组操作
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
// 获取数组长度
count := gjson.Get(json, "store.book.#").Int()
fmt.Println(count) // 2
// 获取所有书名
titles := gjson.Get(json, "store.book.#.title").Array()
for _, title := range titles {
fmt.Println(title.String())
}
// 遍历数组
result := gjson.Get(json, "store.book")
result.ForEach(func(key, value gjson.Result) bool {
fmt.Printf("索引: %s, 书名: %s\n",
key.String(),
value.Get("title").String())
return true // 继续遍历
})
|
5. 高级查询功能
5.1 条件查询
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
json := `{
"users": [
{"name": "张三", "age": 25, "city": "北京"},
{"name": "李四", "age": 30, "city": "上海"},
{"name": "王五", "age": 28, "city": "北京"}
]
}`
// 查找年龄大于28的用户
adults := gjson.Get(json, `users.#[age>28]`)
adults.ForEach(func(key, value gjson.Result) bool {
fmt.Printf("姓名: %s, 年龄: %d\n",
value.Get("name").String(),
value.Get("age").Int())
return true
})
// 多条件查询
beijingUsers := gjson.Get(json, `users.#[city=="北京"&&age>26]`)
|
5.2 通配符查询
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
json := `{
"data": {
"user_1": {"name": "张三", "score": 85},
"user_2": {"name": "李四", "score": 92},
"user_3": {"name": "王五", "score": 78}
}
}`
// 使用通配符获取所有用户
users := gjson.Get(json, "data.*")
users.ForEach(func(key, value gjson.Result) bool {
fmt.Printf("用户: %s, 分数: %d\n",
value.Get("name").String(),
value.Get("score").Int())
return true
})
|
6. 处理API返回数据示例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
|
package main
import (
"fmt"
"log"
"net/http"
"github.com/tidwall/gjson"
)
func main() {
// 调用API
resp, err := http.Get("你的API地址")
if err != nil {
log.Fatal(err)
}
defer resp.Body.Close()
// 读取响应体
body, err := io.ReadAll(resp.Body)
if err != nil {
log.Fatal(err)
}
jsonData := string(body)
// 使用gjson提取数据,无需定义结构体
code := gjson.Get(jsonData, "code").Int()
message := gjson.Get(jsonData, "message").String()
if code == 200 {
// 提取URL字段(避免日期拼接问题)
url := gjson.Get(jsonData, "data.url").String()
title := gjson.Get(jsonData, "data.title").String()
publishTime := gjson.Get(jsonData, "data.publish_time").String()
fmt.Printf("URL: %s\n", url)
fmt.Printf("标题: %s\n", title)
fmt.Printf("发布时间: %s\n", publishTime)
// 存储到Redis
storeToRedis(url, title, publishTime)
} else {
log.Printf("API错误: %s", message)
}
}
func storeToRedis(url, title, publishTime string) {
// 你的Redis存储逻辑
fmt.Printf("存储数据: URL=%s, Title=%s, Time=%s\n", url, title, publishTime)
}
|
7. 多结果和复杂查询
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
|
json := `{
"articles": [
{
"id": 1,
"title": "Go语言入门",
"tags": ["编程", "后端"],
"metadata": {
"views": 1000,
"likes": 50
}
},
{
"id": 2,
"title": "Redis使用技巧",
"tags": ["数据库", "缓存"],
"metadata": {
"views": 2000,
"likes": 80
}
}
]
}`
// 获取所有文章的标题和浏览量
result := gjson.Get(json, "articles.#.{title: title, views: metadata.views}")
result.ForEach(func(key, value gjson.Result) bool {
fmt.Printf("标题: %s, 浏览量: %d\n",
value.Get("title").String(),
value.Get("views").Int())
return true
})
// 查找浏览量大于1500的文章
popular := gjson.Get(json, "articles.#[metadata.views>1500]")
|
8. 错误处理和验证
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
func main() {
json := `{"name": "张三", "age": 25}`
result := gjson.Get(json, "email")
if !result.Exists() {
fmt.Println("email字段不存在")
}
if result.Type == gjson.Null {
fmt.Println("email字段为null")
}
// 安全获取,提供默认值
email := gjson.Get(json, "email").String()
if email == "" {
email = "default@example.com"
}
}
|
9. 性能优化技巧
1
2
3
4
5
6
7
8
9
10
11
12
|
// 预编译路径,提高重复查询性能
const userQuery = "users.#.name"
json := `{"users": [{"name": "张三"}, {"name": "李四"}]}`
// 一次性解析,多次使用
result := gjson.Get(json, userQuery)
names := result.Array()
// 或者使用Parse+Get组合
parsed := gjson.Parse(json)
name1 := parsed.Get("users.0.name").String()
name2 := parsed.Get("users.1.name").String()
|
10. 主要优点
使用 gjson 可以避免结构体字段映射错误,直接提取需要的字段,特别适合处理字段名不确定或经常变化的API响应。