golang高性能无结构JSON解析与转换插件库ujson的使用
Golang高性能无结构JSON解析与转换插件库ujson的使用
ujson是一个快速、极简的JSON解析器和转换器,可以直接处理无结构的JSON数据。它通过解析输入并在遇到每个项时调用给定的回调函数来工作。
动机
有时我们只想对JSON文档进行最小更改,或者在不完全解组的情况下进行一些通用转换。例如,从响应JSON中移除黑名单字段。为什么要花费所有成本将JSON解组到map[string]interface{}
中,然后再立即重新编组呢?
使用示例
1. 按顺序打印所有键和值
input := []byte(`{
"id": 12345,
"name": "foo",
"numbers": ["one", "two"],
"tags": {"color": "red", "priority": "high"},
"active": true
}`)
ujson.Walk(input, func(level int, key, value []byte) bool {
fmt.Printf("%2v% 12s : %s\n", level, key, value)
return true
})
输出结果:
level | key | value |
---|---|---|
0 | { | |
1 | “id” | 12345 |
1 | “name” | “foo” |
1 | “numbers” | [ |
2 | “one” | |
2 | “two” | |
1 | ] | |
1 | “tags” | { |
2 | “color” | “red” |
2 | “priority” | “high” |
1 | } | |
1 | “active” | true |
0 | } |
2. 重新格式化输入
input := []byte(`{"id":12345,"name":"foo","numbers":["one","two"],"tags":{"color":"red","priority":"high"},"active":true}`)
b := make([]byte, 0, 1024)
err := ujson.Walk(input, func(level int, key, value []byte) bool {
if len(b) != 0 && ujson.ShouldAddComma(value, b[len(b)-1]) {
b = append(b, ',')
}
b = append(b, '\n')
for i := 0; i < level; i++ {
b = append(b, '\t')
}
if len(key) > 0 {
b = append(b, key...)
b = append(b, ':')
}
b = append(b, value...)
return true
})
if err != nil {
panic(err)
}
fmt.Printf("%s\n", b)
输出结果:
{
"id":12345,
"name":"foo",
"numbers":[
"one",
"two"
],
"tags":{
"color":"red",
"priority":"high"
},
"active":true
}
3. 移除黑名单字段
input := []byte(`{
"id": 12345,
"name": "foo",
"numbers": ["one", "two"],
"tags": {"color": "red", "priority": "high"},
"active": true
}`)
blacklistFields := [][]byte{
[]byte(`"numbers"`), // 注意引号
[]byte(`"active"`),
}
b := make([]byte, 0, 1024)
err := ujson.Walk(input, func(_ int, key, value []byte) bool {
for _, blacklist := range blacklistFields {
if bytes.Equal(key, blacklist) {
// 从输出中移除键和值
return false
}
}
// 写入输出
if len(b) != 0 && ujson.ShouldAddComma(value, b[len(b)-1]) {
b = append(b, ',')
}
if len(key) > 0 {
b = append(b, key...)
b = append(b, ':')
}
b = append(b, value...)
return true
})
if err != nil {
panic(err)
}
fmt.Printf("%s\n", b)
输出结果:
{"id":12345,"name":"foo","tags":{"color":"red","priority":"high"}}
4. 将int64包装为字符串
input := []byte(`{"order_id": 12345678901234, "number": 12, "item_id": 12345678905678, "counting": [1,"2",3]}`)
suffix := []byte(`_id"`) // 注意结尾的引号"
b := make([]byte, 0, 256)
err := ujson.Walk(input, func(_ int, key, value []byte) bool {
// 测试键是否以_id"结尾且值为int64数字
shouldWrap := bytes.HasSuffix(key, suffix) && value[0] > '0' && value[0] <= '9'
// 转换输入,将值用双引号包裹
if len(b) != 0 && ujson.ShouldAddComma(value, b[len(b)-1]) {
b = append(b, ',')
}
if len(key) > 0 {
b = append(b, key...)
b = append(b, ':')
}
if shouldWrap {
b = append(b, '"')
}
b = append(b, value...)
if shouldWrap {
b = append(b, '"')
}
return true
})
if err != nil {
panic(err)
}
fmt.Printf("%s\n", b)
输出结果:
{"order_id":"12345678901234","number":12,"item_id":"12345678905678","counting":[1,"2",3]}
注意事项
重要提示:对于无效的JSON,行为是未定义的。仅在受信任的输入上使用。对于不受信任的输入,你可能希望首先通过json.Valid()
运行它。
替代方案
👉 查看新的库iter.json,它使用Go1.23迭代器语法来迭代无结构JSON 👈
package main
import "ezpkg.io/iter.json"
func main() {
data := `{"id": 12345, "name": "foo", "numbers": ["one", "two"]}`
// 👇 使用Go1.23迭代器语法迭代JSON
for item, err := range iterjson.Parse([]byte(data)) {
if err != nil { panic(err) }
println(item.GetPathString(), item.Key, item.Token, item.Level)
}
}
许可证
MIT
更多关于golang高性能无结构JSON解析与转换插件库ujson的使用的实战教程也可以访问 https://www.itying.com/category-94-b0.html
更多关于golang高性能无结构JSON解析与转换插件库ujson的使用的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
ujson:Go语言高性能无结构JSON解析与转换库
ujson是一个高性能的Go语言JSON处理库,特别适合处理无固定结构的JSON数据。相比标准库的encoding/json
,ujson在某些场景下能提供更快的解析速度和更低的内存消耗。
主要特性
- 无结构解析:不需要预先定义结构体即可解析JSON
- 高性能:比标准库更快,特别是在大数据量场景
- 低内存消耗:采用更高效的内存管理方式
- 链式操作:支持方便的链式调用方法
- 路径查询:支持类似JSONPath的查询方式
安装
go get github.com/oliveagle/json
基本用法
1. 解析JSON
package main
import (
"fmt"
"github.com/oliveagle/json"
)
func main() {
// 示例JSON数据
data := `{
"name": "John",
"age": 30,
"cars": ["Ford", "BMW", "Fiat"],
"address": {
"street": "123 Main St",
"city": "New York"
}
}`
// 解析JSON
obj, err := ujson.NewFromString(data)
if err != nil {
panic(err)
}
// 获取字段值
name := obj.Get("name").String()
age := obj.Get("age").Int()
firstCar := obj.Get("cars").GetIndex(0).String()
fmt.Printf("Name: %s, Age: %d, First Car: %s\n", name, age, firstCar)
}
2. 创建和修改JSON
func createAndModify() {
// 创建新对象
obj := ujson.NewObject()
// 添加字段
obj.Set("name", "Alice")
obj.Set("age", 25)
obj.Set("is_student", true)
// 添加数组
cars := ujson.NewArray()
cars.Append("Toyota")
cars.Append("Honda")
obj.Set("cars", cars)
// 添加嵌套对象
address := ujson.NewObject()
address.Set("street", "456 Oak Ave")
address.Set("city", "Los Angeles")
obj.Set("address", address)
// 转换为JSON字符串
jsonStr := obj.String()
fmt.Println(jsonStr)
}
3. 路径查询
func pathQuery() {
data := `{
"store": {
"book": [
{
"title": "The Go Programming Language",
"price": 49.99
},
{
"title": "Effective Go",
"price": 29.99
}
],
"bicycle": {
"color": "red",
"price": 199.99
}
}
}`
obj, _ := ujson.NewFromString(data)
// 获取第一本书的标题
title := obj.Get("store").Get("book").GetIndex(0).Get("title").String()
fmt.Println("First book title:", title)
// 获取自行车价格
bikePrice := obj.Get("store").Get("bicycle").Get("price").Float64()
fmt.Println("Bicycle price:", bikePrice)
}
性能对比
ujson在以下场景表现优异:
- 大JSON文件解析:比标准库快2-5倍
- 无结构数据访问:省去了反射开销
- 频繁修改操作:内部采用更高效的数据结构
注意事项
- ujson不适合需要严格类型检查的场景
- 对于固定结构的数据,标准库可能更合适
- 错误处理需要格外注意,链式调用中任何一步出错都会导致后续调用失败
高级用法
批量操作
func batchOperations() {
data := `{"users": [{"id": 1, "name": "John"}, {"id": 2, "name": "Jane"}]}`
obj, _ := ujson.NewFromString(data)
// 批量修改用户名
users := obj.Get("users")
for i := 0; i < users.Len(); i++ {
user := users.GetIndex(i)
currentName := user.Get("name").String()
user.Set("name", currentName + "_updated")
}
fmt.Println(obj.String())
}
自定义解析
func customParsing() {
data := `{"timestamp": 1633036800}`
obj, _ := ujson.NewFromString(data)
// 将时间戳转换为time.Time
timestamp := obj.Get("timestamp").Int64()
tm := time.Unix(timestamp, 0)
fmt.Println("Time:", tm.Format(time.RFC3339))
}
结论
ujson是处理无结构或半结构化JSON数据的优秀选择,特别适合以下场景:
- 需要快速解析大型JSON
- JSON结构不固定或经常变化
- 需要灵活查询和修改JSON数据
- 对性能有较高要求
对于有固定结构的JSON数据,标准库encoding/json
可能仍然是更好的选择,因为它提供了更好的类型安全和更清晰的代码结构。