golang高性能JSON解析与验证插件库fastjson的使用
Golang高性能JSON解析与验证插件库fastjson的使用
fastjson - Go语言的快速JSON解析器和验证器
特性
- 快速:通常比标准库
encoding/json
快15倍 - 无需模式:可以解析任意JSON而无需模式、反射、结构体魔法或代码生成
- 简单API:提供简单易用的API
- 高效访问多个字段:比
jsonparser
和gjson
更高效,因为fastjson
只解析输入JSON一次 - 验证功能:可以验证解析的JSON
- 提取和修改:可以快速提取原始JSON的一部分并进行修改
- 非同构数组:可以解析包含不同类型值的数组
- 保留顺序:保留对象项原始顺序
已知限制
- 需要特别注意对象引用的释放
- 不能从
io.Reader
解析JSON
使用示例
单行代码访问单个字段
s := []byte(`{"foo": [123, "bar"]}`)
fmt.Printf("foo.0=%d\n", fastjson.GetInt(s, "foo", "0"))
// 输出:
// foo.0=123
访问多个字段并处理错误
package main
import (
"fmt"
"github.com/valyala/fastjson"
"log"
)
func main() {
var p fastjson.Parser
v, err := p.Parse(`{
"str": "bar",
"int": 123,
"float": 1.23,
"bool": true,
"arr": [1, "foo", {}]
}`)
if err != nil {
log.Fatal(err)
}
fmt.Printf("str=%s\n", v.GetStringBytes("str"))
fmt.Printf("int=%d\n", v.GetInt("int"))
fmt.Printf("float=%f\n", v.GetFloat64("float"))
fmt.Printf("bool=%v\n", v.GetBool("bool"))
fmt.Printf("arr.1=%s\n", v.GetStringBytes("arr", "1"))
// 输出:
// str=bar
// int=123
// float=1.230000
// bool=true
// arr.1=foo
}
性能优化建议
- 重用
Parser
和Scanner
来解析多个JSON,减少内存分配开销 - 当需要从JSON获取多个字段时,优先使用
Parser
返回的Value
上的Get*
方法 - 对于具有共同前缀路径的字段,先调用
Value.Get
获取共同前缀,然后再调用Get*
方法 - 使用range循环遍历
Value.GetArray
返回的数组,而不是为每个数组项调用Get*
基准测试
Go 1.12下的基准测试结果:
$ GOMAXPROCS=1 go test github.com/valyala/fastjson -bench='Parse$'
goos: linux
goarch: amd64
pkg: github.com/valyala/fastjson
BenchmarkParse/small/stdjson-map 200000 7305 ns/op 26.01 MB/s 960 B/op 51 allocs/op
BenchmarkParse/small/stdjson-struct 500000 3431 ns/op 55.37 MB/s 224 B/op 4 allocs/op
BenchmarkParse/small/stdjson-empty-struct 500000 2273 ns/op 83.58 MB/s 168 B/op 2 allocs/op
BenchmarkParse/small/fastjson 5000000 347 ns/op 547.53 MB/s 0 B/op 0 allocs/op
BenchmarkParse/small/fastjson-get 2000000 620 ns/op 306.39 MB/s 0 B/op 0 allocs/op
安全注意事项
fastjson
在解析攻击者精心构造的输入字符串时不应崩溃或恐慌fastjson
解析inputJSON
字符串最多需要sizeof(Value) * len(inputJSON)
字节的内存
常见问题
Q: 为什么还要创建另一个JSON解析包?
A: 因为其他包要么需要通过结构体和代码生成来定义严格的JSON模式,要么在需要从解析的JSON中获取多个不相关字段时性能不佳。
Q: fastjson的主要用途是什么?
A: 用于RTB和其他JSON-RPC服务的高性能JSON解析。
Q: 为什么fastjson不提供快速的编组(序列化)?
A: 它确实提供了一些编组功能,但对于高性能JSON编组,建议使用quicktemplate。
更多关于golang高性能JSON解析与验证插件库fastjson的使用的实战教程也可以访问 https://www.itying.com/category-94-b0.html
1 回复
更多关于golang高性能JSON解析与验证插件库fastjson的使用的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
Go高性能JSON解析与验证:fastjson库使用指南
fastjson是一个高性能的Go语言JSON解析库,相比标准库的encoding/json
,它在处理大量JSON数据时能提供更好的性能。下面我将详细介绍fastjson的使用方法。
安装fastjson
go get github.com/valyala/fastjson
基本使用方法
1. 解析JSON字符串
package main
import (
"fmt"
"github.com/valyala/fastjson"
)
func main() {
var p fastjson.Parser
jsonData := `{"name": "John", "age": 30, "skills": ["Go", "Python"]}`
v, err := p.Parse(jsonData)
if err != nil {
panic(err)
}
fmt.Printf("Name: %s\n", v.GetStringBytes("name"))
fmt.Printf("Age: %d\n", v.GetInt("age"))
skills := v.GetArray("skills")
for i, skill := range skills {
fmt.Printf("Skill %d: %s\n", i+1, skill.String())
}
}
2. 创建JSON对象
package main
import (
"fmt"
"github.com/valyala/fastjson"
)
func main() {
var a fastjson.Arena
obj := a.NewObject()
obj.Set("name", a.NewString("Alice"))
obj.Set("age", a.NewNumberInt(28))
skills := a.NewArray()
skills.SetArrayItem(0, a.NewString("Java"))
skills.SetArrayItem(1, a.NewString("JavaScript"))
obj.Set("skills", skills)
fmt.Println(obj.String())
}
高级功能
1. 验证JSON结构
package main
import (
"fmt"
"github.com/valyala/fastjson"
)
func main() {
schema := `{
"type": "object",
"properties": {
"name": {"type": "string"},
"age": {"type": "number"},
"skills": {
"type": "array",
"items": {"type": "string"}
}
},
"required": ["name", "age"]
}`
var p fastjson.Parser
jsonData := `{"name": "Bob", "age": 35, "skills": ["C++", "Rust"]}`
v, err := p.Parse(jsonData)
if err != nil {
panic(err)
}
// 验证JSON结构
if err := fastjson.Validate(schema, v); err != nil {
fmt.Printf("Validation error: %v\n", err)
} else {
fmt.Println("JSON is valid")
}
}
2. 高性能解析大量JSON数据
package main
import (
"fmt"
"github.com/valyala/fastjson"
)
func main() {
jsonData := `[
{"id": 1, "value": "data1"},
{"id": 2, "value": "data2"},
{"id": 3, "value": "data3"}
]`
var p fastjson.Parser
v, err := p.Parse(jsonData)
if err != nil {
panic(err)
}
items := v.GetArray()
for _, item := range items {
id := item.GetInt("id")
value := item.GetStringBytes("value")
fmt.Printf("ID: %d, Value: %s\n", id, value)
}
}
性能优化技巧
- 复用Parser和Arena对象:避免频繁创建和销毁这些对象
- 使用GetStringBytes代替GetString:减少内存分配
- 直接访问字段:避免多次调用Get方法
package main
import (
"fmt"
"github.com/valyala/fastjson"
)
func main() {
var p fastjson.Parser
var a fastjson.Arena
// 复用Parser解析多个JSON
json1 := `{"key": "value1"}`
json2 := `{"key": "value2"}`
v1, _ := p.Parse(json1)
v2, _ := p.Parse(json2)
fmt.Println(v1.GetStringBytes("key"))
fmt.Println(v2.GetStringBytes("key"))
// 复用Arena创建多个JSON对象
obj1 := a.NewObject()
obj1.Set("id", a.NewNumberInt(1))
obj2 := a.NewObject()
obj2.Set("id", a.NewNumberInt(2))
fmt.Println(obj1.String())
fmt.Println(obj2.String())
}
与标准库对比
fastjson相比标准库encoding/json
有以下优势:
- 更快的解析速度:特别是对于大型JSON数据
- 更低的内存使用:减少内存分配次数
- 流式处理能力:可以处理部分JSON数据
但需要注意:
- API与标准库不同,学习曲线稍陡
- 某些情况下错误信息不如标准库详细
总结
fastjson是处理高性能JSON需求的优秀选择,特别适合:
- 需要处理大量JSON数据的场景
- 对性能要求严格的应用程序
- 需要验证JSON结构的场景
对于简单的JSON处理,标准库可能更合适,但在性能关键路径上,fastjson能带来显著的性能提升。