1 介绍
1.1 定义
01.GJSON简介
a.基本定义
GJSON是一个Go语言的JSON解析库,提供快速获取JSON值的能力。
b.项目来源
由tidwall开源,GitHub: github.com/tidwall/gjson。
c.核心特点
高性能、简洁API、无需反序列化、支持路径查询。
02.设计目标
a.快速读取
直接从JSON字符串中提取值,无需完整解析。
b.零依赖
纯Go实现,无外部依赖。
c.简单易用
一行代码即可获取深层嵌套的值。
03.基本使用
a.功能说明
演示GJSON的基本用法。
b.代码示例
---
package main
import (
"fmt"
"github.com/tidwall/gjson"
)
func main() {
json := `{
"name": "Alice",
"age": 25,
"city": "Beijing",
"tags": ["developer", "golang"]
}`
// 直接获取值
name := gjson.Get(json, "name")
age := gjson.Get(json, "age")
city := gjson.Get(json, "city")
fmt.Printf("Name: %s\n", name.String())
fmt.Printf("Age: %d\n", age.Int())
fmt.Printf("City: %s\n", city.String())
// 获取数组元素
tag := gjson.Get(json, "tags.0")
fmt.Printf("First tag: %s\n", tag.String())
}
---
1.2 核心概念
01.Result对象
a.返回类型
Get方法返回gjson.Result对象。
b.值提取
通过String()、Int()、Float()等方法获取实际值。
c.类型判断
通过Type属性判断值类型。
02.路径语法
a.点号分隔
使用点号访问嵌套对象,如"user.name"。
b.数组索引
使用数字索引访问数组,如"tags.0"。
c.通配符
支持#通配符查询,如"users.#.name"。
03.核心方法
a.Get获取
gjson.Get(json, path) 获取单个值。
b.GetMany批量
gjson.GetMany(json, path1, path2) 批量获取。
c.Parse解析
gjson.Parse(json) 解析整个JSON。
d.Valid验证
gjson.Valid(json) 验证JSON格式。
1.3 优缺点
01.主要优势
a.性能优异
比标准库encoding/json快5-10倍。
b.无需struct
不需要定义结构体,直接路径访问。
c.简洁API
一行代码获取深层嵌套值。
d.零内存分配
路径查询几乎无内存分配。
e.容错性强
路径不存在返回空值,不会panic。
02.主要限制
a.只读操作
GJSON主要用于读取,修改需要配合SJSON。
b.不支持流式
需要完整的JSON字符串。
c.大文件性能
超大JSON文件性能会下降。
03.适用场景
a.适合使用
API响应解析、配置文件读取、日志分析、快速原型开发。
b.不适合使用
需要频繁修改JSON、需要严格类型检查、超大JSON文件(>100MB)。
1.4 使用场景
01.API响应解析
a.HTTP响应
快速解析REST API返回的JSON。
b.第三方接口
解析外部API数据。
c.API示例
a.功能说明
解析GitHub API响应。
b.代码示例
---
package main
import (
"fmt"
"io/ioutil"
"net/http"
"github.com/tidwall/gjson"
)
func main() {
resp, _ := http.Get("https://api.github.com/users/golang")
body, _ := ioutil.ReadAll(resp.Body)
defer resp.Body.Close()
json := string(body)
// 提取关键信息
name := gjson.Get(json, "name")
followers := gjson.Get(json, "followers")
repos := gjson.Get(json, "public_repos")
bio := gjson.Get(json, "bio")
fmt.Printf("Name: %s\n", name.String())
fmt.Printf("Followers: %d\n", followers.Int())
fmt.Printf("Repos: %d\n", repos.Int())
fmt.Printf("Bio: %s\n", bio.String())
}
---
02.配置文件读取
a.JSON配置
读取JSON格式的配置文件。
b.动态配置
无需预定义结构体读取配置。
03.日志分析
a.结构化日志
解析JSON格式的日志。
b.快速提取
从大量日志中提取关键字段。
04.数据提取
a.爬虫数据
从网页API提取数据。
b.数据清洗
快速提取需要的字段。
1.5 设计理念
01.性能优先
a.零拷贝
直接在原始字符串上操作,避免拷贝。
b.惰性解析
只解析需要的部分,不完整解析。
c.最小分配
尽量减少内存分配。
02.简洁API
a.一行代码
单行代码完成复杂路径访问。
b.链式调用
支持方法链式调用。
c.容错设计
错误情况返回零值,不panic。
03.设计示例
a.功能说明
展示GJSON的设计优势。
b.代码示例
---
package main
import (
"fmt"
"github.com/tidwall/gjson"
)
func main() {
json := `{
"user": {
"name": "Alice",
"profile": {
"age": 25,
"address": {
"city": "Beijing",
"street": "Changan Ave"
}
}
}
}`
// 一行代码访问深层嵌套
city := gjson.Get(json, "user.profile.address.city")
fmt.Printf("City: %s\n", city.String())
// 路径不存在也不会panic
notExist := gjson.Get(json, "user.profile.phone")
fmt.Printf("Phone exists: %v\n", notExist.Exists())
fmt.Printf("Phone value: %s\n", notExist.String())
// 类型安全获取
age := gjson.Get(json, "user.profile.age")
if age.Exists() {
fmt.Printf("Age: %d\n", age.Int())
}
}
---
1.6 性能特点
01.性能对比
a.vs encoding/json
读取速度快5-10倍。
b.vs其他库
比fastjson、jsoniter在简单查询上更快。
c.内存使用
几乎零内存分配。
02.性能优化
a.路径缓存
重复查询同一路径可以缓存Result。
b.批量查询
使用GetMany批量获取多个值。
c.Parse预解析
频繁查询同一JSON可先Parse。
03.性能测试
a.功能说明
测试GJSON性能表现。
b.代码示例
---
package main
import (
"fmt"
"time"
"github.com/tidwall/gjson"
)
func main() {
json := `{
"users": [
{"name": "Alice", "age": 25},
{"name": "Bob", "age": 30},
{"name": "Charlie", "age": 35}
]
}`
// 单次查询性能
start := time.Now()
for i := 0; i < 100000; i++ {
gjson.Get(json, "users.0.name")
}
elapsed := time.Since(start)
fmt.Printf("100k queries: %v\n", elapsed)
// Parse后查询性能
parsed := gjson.Parse(json)
start = time.Now()
for i := 0; i < 100000; i++ {
parsed.Get("users.0.name")
}
elapsed = time.Since(start)
fmt.Printf("100k parsed queries: %v\n", elapsed)
// 批量查询
start = time.Now()
for i := 0; i < 100000; i++ {
gjson.GetMany(json, "users.0.name", "users.1.name", "users.2.name")
}
elapsed = time.Since(start)
fmt.Printf("100k batch queries: %v\n", elapsed)
}
---
04.性能建议
a.重复查询
使用Parse预解析。
b.批量读取
使用GetMany代替多次Get。
c.避免不必要的类型转换
只在需要时转换类型。
2 基础操作
2.1 汇总:6个
01.Get获取值
a.单值获取
gjson.Get(json, path) 获取单个值。
b.返回Result
返回gjson.Result对象。
02.路径语法
a.点号路径
user.name访问嵌套对象。
b.数组索引
users.0访问数组元素。
c.通配符
users.#.name获取所有name。
03.类型判断
a.Type属性
result.Type判断值类型。
b.Exists检查
result.Exists()检查是否存在。
04.数组访问
a.索引访问
通过数字索引访问元素。
b.Array()方法
获取整个数组。
05.对象访问
a.Map()方法
获取对象的所有键值对。
b.遍历对象
ForEach遍历对象或数组。
06.默认值处理
a.零值返回
不存在的路径返回零值。
b.容错设计
不会panic,安全访问。
2.2 获取值Get
01.Get方法
a.基本用法
gjson.Get(json, path)获取值。
b.返回类型
返回gjson.Result对象。
02.Get示例
a.功能说明
演示Get方法的各种用法。
b.代码示例
---
package main
import (
"fmt"
"github.com/tidwall/gjson"
)
func main() {
json := `{
"name": "Alice",
"age": 25,
"email": "[email protected]",
"address": {
"city": "Beijing",
"zipcode": "100000"
},
"hobbies": ["reading", "coding", "music"]
}`
// 获取基本类型
name := gjson.Get(json, "name")
fmt.Printf("Name: %s\n", name.String())
age := gjson.Get(json, "age")
fmt.Printf("Age: %d\n", age.Int())
// 获取嵌套对象
city := gjson.Get(json, "address.city")
fmt.Printf("City: %s\n", city.String())
// 获取数组元素
firstHobby := gjson.Get(json, "hobbies.0")
fmt.Printf("First hobby: %s\n", firstHobby.String())
// 获取整个对象
address := gjson.Get(json, "address")
fmt.Printf("Address: %s\n", address.Raw)
// 获取整个数组
hobbies := gjson.Get(json, "hobbies")
fmt.Printf("Hobbies: %s\n", hobbies.Raw)
}
---
03.GetMany批量获取
a.功能说明
一次获取多个值。
b.代码示例
---
package main
import (
"fmt"
"github.com/tidwall/gjson"
)
func main() {
json := `{
"name": "Alice",
"age": 25,
"city": "Beijing"
}`
// 批量获取多个值
results := gjson.GetMany(json, "name", "age", "city")
fmt.Printf("Name: %s\n", results[0].String())
fmt.Printf("Age: %d\n", results[1].Int())
fmt.Printf("City: %s\n", results[2].String())
}
---
2.3 路径语法
01.点号分隔
a.对象访问
使用点号访问嵌套对象。
b.多层嵌套
支持任意深度嵌套。
02.路径示例
a.功能说明
演示各种路径语法。
b.代码示例
---
package main
import (
"fmt"
"github.com/tidwall/gjson"
)
func main() {
json := `{
"user": {
"name": "Alice",
"profile": {
"age": 25,
"contacts": {
"email": "[email protected]",
"phone": "123456"
}
},
"friends": ["Bob", "Charlie", "David"]
}
}`
// 简单路径
name := gjson.Get(json, "user.name")
fmt.Printf("Name: %s\n", name.String())
// 深层嵌套
email := gjson.Get(json, "user.profile.contacts.email")
fmt.Printf("Email: %s\n", email.String())
// 数组索引
firstFriend := gjson.Get(json, "user.friends.0")
fmt.Printf("First friend: %s\n", firstFriend.String())
// 组合路径
age := gjson.Get(json, "user.profile.age")
fmt.Printf("Age: %d\n", age.Int())
}
---
03.转义字符
a.特殊字符
键名包含点号需要转义。
b.转义语法
使用反斜杠转义特殊字符。
3 路径查询
3.1 点号路径
01.基本语法
a.点号分隔
使用点号访问嵌套字段。
b.示例
user.name, user.profile.age
02.点号路径示例
a.功能说明
使用点号路径访问JSON。
b.代码示例
---
package main
import (
"fmt"
"github.com/tidwall/gjson"
)
func main() {
json := `{
"company": {
"name": "TechCorp",
"location": {
"country": "China",
"city": "Beijing",
"address": {
"street": "Zhongguancun",
"building": "Building A"
}
},
"employees": 1000
}
}`
// 一级路径
companyName := gjson.Get(json, "company.name")
fmt.Printf("Company: %s\n", companyName.String())
// 二级路径
country := gjson.Get(json, "company.location.country")
fmt.Printf("Country: %s\n", country.String())
// 三级路径
city := gjson.Get(json, "company.location.city")
fmt.Printf("City: %s\n", city.String())
// 四级路径
street := gjson.Get(json, "company.location.address.street")
fmt.Printf("Street: %s\n", street.String())
building := gjson.Get(json, "company.location.address.building")
fmt.Printf("Building: %s\n", building.String())
}
---
3.2 数组索引
01.索引语法
a.数字索引
使用数字访问数组元素。
b.负数索引
-1表示最后一个元素。
02.数组索引示例
a.功能说明
访问数组元素的各种方式。
b.代码示例
---
package main
import (
"fmt"
"github.com/tidwall/gjson"
)
func main() {
json := `{
"users": [
{"name": "Alice", "age": 25},
{"name": "Bob", "age": 30},
{"name": "Charlie", "age": 35}
],
"tags": ["go", "json", "parser"]
}`
// 访问第一个元素
firstUser := gjson.Get(json, "users.0.name")
fmt.Printf("First user: %s\n", firstUser.String())
// 访问第二个元素
secondUser := gjson.Get(json, "users.1.name")
fmt.Printf("Second user: %s\n", secondUser.String())
// 访问最后一个元素
lastUser := gjson.Get(json, "users.2.name")
fmt.Printf("Last user: %s\n", lastUser.String())
// 访问简单数组
firstTag := gjson.Get(json, "tags.0")
fmt.Printf("First tag: %s\n", firstTag.String())
secondTag := gjson.Get(json, "tags.1")
fmt.Printf("Second tag: %s\n", secondTag.String())
// 获取数组长度
usersArray := gjson.Get(json, "users")
fmt.Printf("Users count: %d\n", len(usersArray.Array()))
}
---
03.数组遍历
a.Array()方法
获取所有数组元素。
b.ForEach遍历
遍历数组每个元素。
3.3 通配符
01.#通配符
a.数组长度
users.# 获取数组长度。
b.所有元素
users.#.name 获取所有name字段。
02.通配符示例
a.功能说明
使用通配符查询数组。
b.代码示例
---
package main
import (
"fmt"
"github.com/tidwall/gjson"
)
func main() {
json := `{
"users": [
{"name": "Alice", "age": 25, "city": "Beijing"},
{"name": "Bob", "age": 30, "city": "Shanghai"},
{"name": "Charlie", "age": 35, "city": "Shenzhen"}
]
}`
// 获取数组长度
count := gjson.Get(json, "users.#")
fmt.Printf("User count: %d\n", count.Int())
// 获取所有name
names := gjson.Get(json, "users.#.name")
fmt.Println("All names:")
for _, name := range names.Array() {
fmt.Printf(" - %s\n", name.String())
}
// 获取所有age
ages := gjson.Get(json, "users.#.age")
fmt.Println("All ages:")
for _, age := range ages.Array() {
fmt.Printf(" - %d\n", age.Int())
}
// 获取所有city
cities := gjson.Get(json, "users.#.city")
fmt.Println("All cities:")
for _, city := range cities.Array() {
fmt.Printf(" - %s\n", city.String())
}
}
---
3.4 条件查询
01.条件语法
a.#(...)#
条件过滤语法。
b.比较运算符
==, !=, <, >, <=, >=
02.条件查询示例
a.功能说明
使用条件过滤数组元素。
b.代码示例
---
package main
import (
"fmt"
"github.com/tidwall/gjson"
)
func main() {
json := `{
"users": [
{"name": "Alice", "age": 25, "active": true},
{"name": "Bob", "age": 30, "active": false},
{"name": "Charlie", "age": 35, "active": true},
{"name": "David", "age": 28, "active": true}
]
}`
// 查询age>28的用户
result := gjson.Get(json, "users.#(age>28)#.name")
fmt.Println("Users with age > 28:")
for _, name := range result.Array() {
fmt.Printf(" - %s\n", name.String())
}
// 查询active=true的用户
activeUsers := gjson.Get(json, "users.#(active==true)#.name")
fmt.Println("\nActive users:")
for _, name := range activeUsers.Array() {
fmt.Printf(" - %s\n", name.String())
}
// 查询name="Bob"的用户
bob := gjson.Get(json, `users.#(name=="Bob")#`)
fmt.Printf("\nBob's info: %s\n", bob.Raw)
// 多条件查询
result2 := gjson.Get(json, "users.#(age>=28 && active==true)#.name")
fmt.Println("\nUsers: age>=28 and active:")
for _, name := range result2.Array() {
fmt.Printf(" - %s\n", name.String())
}
}
---
3.5 嵌套查询
01.嵌套路径
a.多层嵌套
支持任意深度的嵌套查询。
b.组合查询
结合数组、对象、条件查询。
02.嵌套查询示例
a.功能说明
复杂嵌套结构的查询。
b.代码示例
---
package main
import (
"fmt"
"github.com/tidwall/gjson"
)
func main() {
json := `{
"company": {
"departments": [
{
"name": "Engineering",
"teams": [
{
"name": "Backend",
"members": [
{"name": "Alice", "role": "Lead"},
{"name": "Bob", "role": "Developer"}
]
},
{
"name": "Frontend",
"members": [
{"name": "Charlie", "role": "Lead"},
{"name": "David", "role": "Developer"}
]
}
]
}
]
}
}`
// 获取第一个部门名称
deptName := gjson.Get(json, "company.departments.0.name")
fmt.Printf("Department: %s\n", deptName.String())
// 获取Backend团队的所有成员名字
backendMembers := gjson.Get(json, "company.departments.0.teams.0.members.#.name")
fmt.Println("Backend members:")
for _, name := range backendMembers.Array() {
fmt.Printf(" - %s\n", name.String())
}
// 获取所有Lead角色的人
leads := gjson.Get(json, `company.departments.0.teams.#.members.#(role=="Lead")#.name`)
fmt.Println("\nAll leads:")
for _, team := range leads.Array() {
for _, name := range team.Array() {
fmt.Printf(" - %s\n", name.String())
}
}
}
---
3.6 多值查询
01.GetMany方法
a.批量查询
一次查询多个路径。
b.性能优化
比多次Get更高效。
02.多值查询示例
a.功能说明
批量获取多个字段。
b.代码示例
---
package main
import (
"fmt"
"github.com/tidwall/gjson"
)
func main() {
json := `{
"user": {
"name": "Alice",
"age": 25,
"email": "[email protected]",
"address": {
"city": "Beijing",
"country": "China"
}
},
"timestamp": 1234567890
}`
// 批量获取多个值
results := gjson.GetMany(json,
"user.name",
"user.age",
"user.email",
"user.address.city",
"timestamp",
)
fmt.Printf("Name: %s\n", results[0].String())
fmt.Printf("Age: %d\n", results[1].Int())
fmt.Printf("Email: %s\n", results[2].String())
fmt.Printf("City: %s\n", results[3].String())
fmt.Printf("Timestamp: %d\n", results[4].Int())
// 遍历结果
paths := []string{"user.name", "user.age", "user.email"}
results2 := gjson.GetMany(json, paths...)
for i, result := range results2 {
fmt.Printf("%s: %s\n", paths[i], result.String())
}
}
---
4 高级特性
4.1 ForEach遍历
01.ForEach方法
a.遍历数组
遍历JSON数组的每个元素。
b.遍历对象
遍历JSON对象的每个键值对。
02.ForEach示例
a.功能说明
使用ForEach遍历JSON。
b.代码示例
---
package main
import (
"fmt"
"github.com/tidwall/gjson"
)
func main() {
json := `{
"users": [
{"name": "Alice", "age": 25},
{"name": "Bob", "age": 30},
{"name": "Charlie", "age": 35}
],
"config": {
"timeout": 30,
"retry": 3,
"debug": true
}
}`
// 遍历数组
fmt.Println("Users:")
gjson.Get(json, "users").ForEach(func(key, value gjson.Result) bool {
name := value.Get("name").String()
age := value.Get("age").Int()
fmt.Printf(" %s: %d years old\n", name, age)
return true // 继续遍历
})
// 遍历对象
fmt.Println("\nConfig:")
gjson.Get(json, "config").ForEach(func(key, value gjson.Result) bool {
fmt.Printf(" %s: %v\n", key.String(), value.Value())
return true
})
}
---
4.2 Parse解析
01.Parse方法
a.预解析
gjson.Parse(json)预解析JSON。
b.重复查询
Parse后的Result可重复查询。
02.Parse示例
a.功能说明
使用Parse提升性能。
b.代码示例
---
package main
import (
"fmt"
"github.com/tidwall/gjson"
)
func main() {
json := `{
"user": {
"name": "Alice",
"age": 25,
"email": "[email protected]"
},
"posts": [
{"title": "Post 1", "views": 100},
{"title": "Post 2", "views": 200}
]
}`
// 预解析JSON
parsed := gjson.Parse(json)
// 多次查询
name := parsed.Get("user.name")
age := parsed.Get("user.age")
email := parsed.Get("user.email")
fmt.Printf("Name: %s\n", name.String())
fmt.Printf("Age: %d\n", age.Int())
fmt.Printf("Email: %s\n", email.String())
// 查询数组
posts := parsed.Get("posts")
fmt.Println("\nPosts:")
posts.ForEach(func(key, value gjson.Result) bool {
title := value.Get("title").String()
views := value.Get("views").Int()
fmt.Printf(" %s: %d views\n", title, views)
return true
})
}
---
4.3 Valid验证
01.Valid方法
a.JSON验证
gjson.Valid(json)验证JSON格式。
b.返回bool
有效返回true,无效返回false。
02.Valid示例
a.功能说明
验证JSON字符串是否有效。
b.代码示例
---
package main
import (
"fmt"
"github.com/tidwall/gjson"
)
func main() {
// 有效的JSON
validJson := `{"name": "Alice", "age": 25}`
fmt.Printf("Valid JSON: %v\n", gjson.Valid(validJson))
// 无效的JSON
invalidJson := `{"name": "Alice", "age": 25`
fmt.Printf("Invalid JSON: %v\n", gjson.Valid(invalidJson))
// 空字符串
emptyJson := ``
fmt.Printf("Empty JSON: %v\n", gjson.Valid(emptyJson))
// 只有空格
spaceJson := ` `
fmt.Printf("Space JSON: %v\n", gjson.Valid(spaceJson))
// 数组
arrayJson := `[1, 2, 3]`
fmt.Printf("Array JSON: %v\n", gjson.Valid(arrayJson))
// 使用前验证
json := `{"user": {"name": "Bob"}}`
if gjson.Valid(json) {
name := gjson.Get(json, "user.name")
fmt.Printf("User name: %s\n", name.String())
} else {
fmt.Println("Invalid JSON format")
}
}
---
5 实战应用
5.1 安装使用
01.安装GJSON
a.go get安装
获取GJSON库。
b.代码示例
---
go get -u github.com/tidwall/gjson
---
02.导入使用
a.导入包
import "github.com/tidwall/gjson"
b.快速开始
a.功能说明
最简单的GJSON使用示例。
b.代码示例
---
package main
import (
"fmt"
"github.com/tidwall/gjson"
)
func main() {
json := `{"name":"Alice","age":25}`
name := gjson.Get(json, "name")
age := gjson.Get(json, "age")
fmt.Printf("Name: %s, Age: %d\n", name.String(), age.Int())
}
---
03.配合SJSON
a.安装SJSON
go get -u github.com/tidwall/sjson
b.读写结合
GJSON读取,SJSON修改。
5.2 API响应解析
01.HTTP API解析
a.REST API
解析HTTP API返回的JSON。
b.提取数据
快速提取需要的字段。
02.API解析示例
a.功能说明
解析真实API响应。
b.代码示例
---
package main
import (
"fmt"
"io/ioutil"
"net/http"
"github.com/tidwall/gjson"
)
func main() {
// 请求GitHub API
resp, err := http.Get("https://api.github.com/repos/tidwall/gjson")
if err != nil {
panic(err)
}
defer resp.Body.Close()
body, _ := ioutil.ReadAll(resp.Body)
json := string(body)
// 提取关键信息
name := gjson.Get(json, "name")
stars := gjson.Get(json, "stargazers_count")
forks := gjson.Get(json, "forks_count")
language := gjson.Get(json, "language")
description := gjson.Get(json, "description")
fmt.Printf("Repository: %s\n", name.String())
fmt.Printf("Stars: %d\n", stars.Int())
fmt.Printf("Forks: %d\n", forks.Int())
fmt.Printf("Language: %s\n", language.String())
fmt.Printf("Description: %s\n", description.String())
// 提取owner信息
ownerLogin := gjson.Get(json, "owner.login")
ownerType := gjson.Get(json, "owner.type")
fmt.Printf("\nOwner: %s (%s)\n", ownerLogin.String(), ownerType.String())
}
---