高性能的 Go JSON 解析库

给大家分享一个高性能的 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. 主要优点

  • 无需定义结构体:动态处理任何JSON格式

  • 路径查询强大:支持条件查询、通配符、数组操作

  • 性能优秀:比标准库的 json.Unmarshal 更快

  • 错误容忍:字段不存在时不会报错

  • 链式调用:简洁的API设计

使用 gjson 可以避免结构体字段映射错误,直接提取需要的字段,特别适合处理字段名不确定或经常变化的API响应。

0%