valyala/fastjson

GitHub: valyala/fastjson

一款高性能的 Go 语言 JSON 解析器与验证器,无需 struct、反射或代码生成即可快速解析和验证任意 JSON。

Stars: 2462 | Forks: 157

[![构建状态](https://travis-ci.org/valyala/fastjson.svg)](https://travis-ci.org/valyala/fastjson) [![GoDoc](https://godoc.org/github.com/valyala/fastjson?status.svg)](http://godoc.org/github.com/valyala/fastjson) [![Go Report](https://goreportcard.com/badge/github.com/valyala/fastjson)](https://goreportcard.com/report/github.com/valyala/fastjson) [![codecov](https://codecov.io/gh/valyala/fastjson/branch/master/graph/badge.svg)](https://codecov.io/gh/valyala/fastjson) # fastjson - 适用于 Go 的快速 JSON 解析器和验证器 ## 特性 * 快速。通常比标准的 [encoding/json](https://golang.org/pkg/encoding/json/) 快 15 倍。 参见[基准测试](#benchmarks)。 * 解析任意 JSON,无需 schema、reflection、struct magic 和代码生成, 这与 [easyjson](https://github.com/mailru/easyjson) 不同。 * 提供简单的 [API](http://godoc.org/github.com/valyala/fastjson)。 * 在访问多个不相关的字段时,性能优于 [jsonparser](https://github.com/buger/jsonparser) 和 [gjson](https://github.com/tidwall/gjson), 因为 `fastjson` 只解析输入的 JSON 一次。 * 与 [jsonparser](https://github.com/buger/jsonparser) 和 [gjson](https://github.com/tidwall/gjson) 不同,它会验证解析的 JSON。 * 可以使用 `Value.Get(...).MarshalTo` 快速提取原始 JSON 的一部分, 并使用 [Del](https://godoc.org/github.com/valyala/fastjson#Value.Del) 和 [Set](https://godoc.org/github.com/valyala/fastjson#Value.Set) 函数进行修改。 * 可以解析包含不同类型值的数组(即非同质类型)。 例如,`fastjson` 可以轻松解析以下 JSON 数组 `[123, "foo", [456], {"k": "v"}, null]`。 * 调用 [Object.Visit](https://godoc.org/github.com/valyala/fastjson#Object.Visit) 时,`fastjson` 会保持对象项的原始顺序。 ## 已知限制 * 使用时需要格外小心 —— [Parser](https://godoc.org/github.com/valyala/fastjson#Parser) 递归返回的某些对象的引用 必须在下一次调用 [Parse](https://godoc.org/github.com/valyala/fastjson#Parser.Parse) 之前被释放。 否则程序可能无法正常工作。这同样适用于 [Arena](https://godoc.org/github.com/valyala/fastjson#Arena) 返回的对象。 请遵循[文档](https://godoc.org/github.com/valyala/fastjson)中的建议。 * 无法从 `io.Reader` 解析 JSON。如果需要从字符串中解析 JSON 值流,可以使用 [Scanner](https://godoc.org/github.com/valyala/fastjson#Scanner)。 ## 用法 访问单个字段的单行代码: ``` s := []byte(`{"foo": [123, "bar"]}`) fmt.Printf("foo.0=%d\n", fastjson.GetInt(s, "foo", "0")) // Output: // foo.0=123 ``` 带有错误处理的访问多个字段: ``` 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("foo=%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")) // Output: // foo=bar // int=123 // float=1.230000 // bool=true // arr.1=foo ``` 另请参见[示例](https://godoc.org/github.com/valyala/fastjson#pkg-examples)。 ## 安全性 * `fastjson` 在解析由攻击者特殊构造的输入字符串时,不应崩溃或 panic。它必须对无效的输入 JSON 返回错误。 * `fastjson` 解析 `inputJSON` 字符串时需要高达 `sizeof(Value) * len(inputJSON)` 字节的内存。 在解析之前限制 `inputJSON` 的最大大小,以限制最大内存使用量。 ## 性能优化技巧 * 重用 [Parser](https://godoc.org/github.com/valyala/fastjson#Parser) 和 [Scanner](https://godoc.org/github.com/valyala/fastjson#Scanner) 来解析多个 JSON。这减少了内存分配的开销。 在这种情况下,[ParserPool](https://godoc.org/github.com/valyala/fastjson#ParserPool) 可能会有用。 * 当必须从 JSON 中获取多个字段时,优先对 [Parser](https://godoc.org/github.com/valyala/fastjson#Parser) 返回的值调用 `Value.Get*`, 而不是调用 `Get*` 单行代码,因为每个 `Get*` 单行代码都会重新解析输入的 JSON。 * 对于公共前缀路径,优先调用一次 [Value.Get](https://godoc.org/github.com/valyala/fastjson#Value.Get), 然后对返回的值针对不同的后缀路径调用 `Value.Get*`。 * 优先使用 range 循环遍历从 [Value.GetArray](https://godoc.org/github.com/valyala/fastjson#Object.Visit) 返回的数组, 而不是对每个数组项调用 `Value.Get*`。 ## 模糊测试 安装 [go-fuzz](https://github.com/dvyukov/go-fuzz) 以及可选的 go-fuzz-corpus。 ``` go get -u github.com/dvyukov/go-fuzz/go-fuzz github.com/dvyukov/go-fuzz/go-fuzz-build ``` 使用 `go-fuzz-build` 构建并使用可选的语料库运行 `go-fuzz`。 ``` mkdir -p workdir/corpus cp $GOPATH/src/github.com/dvyukov/go-fuzz-corpus/json/corpus/* workdir/corpus go-fuzz-build github.com/valyala/fastjson go-fuzz -bin=fastjson-fuzz.zip -workdir=workdir ``` ## 基准测试 基准测试使用了 Go 1.12。 图例: * `small` - 解析 [small.json](testdata/small.json) (190 bytes)。 * `medium` - 解析 [medium.json](testdata/medium.json) (2.3KB)。 * `large` - 解析 [large.json](testdata/large.json) (28KB)。 * `canada` - 解析 [canada.json](testdata/canada.json) (2.2MB)。 * `citm` - 解析 [citm_catalog.json](testdata/citm_catalog.json) (1.7MB)。 * `twitter` - 解析 [twitter.json](testdata/twitter.json) (617KB)。 * `stdjson-map` - 使用 `encoding/json` 解析为 `map[string]interface{}`。 * `stdjson-struct` - 使用 `encoding/json` 解析为包含所解析 JSON 字段子集的 struct。 * `stdjson-empty-struct` - 使用 `encoding/json` 解析为空 struct。 这是 `encoding/json` 可能的最快解决方案,可用于 JSON 验证。另请参见 JSON 验证的基准测试结果。 * `fastjson` - 使用 `fastjson` 解析,不访问字段。 * `fastjson-get` - 使用 `fastjson` 解析并访问字段,类似于 `stdjson-struct`。 ``` $ 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 BenchmarkParse/medium/stdjson-map 30000 40672 ns/op 57.26 MB/s 10196 B/op 208 allocs/op BenchmarkParse/medium/stdjson-struct 30000 47792 ns/op 48.73 MB/s 9174 B/op 258 allocs/op BenchmarkParse/medium/stdjson-empty-struct 100000 22096 ns/op 105.40 MB/s 280 B/op 5 allocs/op BenchmarkParse/medium/fastjson 500000 3025 ns/op 769.90 MB/s 0 B/op 0 allocs/op BenchmarkParse/medium/fastjson-get 500000 3211 ns/op 725.20 MB/s 0 B/op 0 allocs/op BenchmarkParse/large/stdjson-map 2000 614079 ns/op 45.79 MB/s 210734 B/op 2785 allocs/op BenchmarkParse/large/stdjson-struct 5000 298554 ns/op 94.18 MB/s 15616 B/op 353 allocs/op BenchmarkParse/large/stdjson-empty-struct 5000 268577 ns/op 104.69 MB/s 280 B/op 5 allocs/op BenchmarkParse/large/fastjson 50000 35210 ns/op 798.56 MB/s 5 B/op 0 allocs/op BenchmarkParse/large/fastjson-get 50000 35171 ns/op 799.46 MB/s 5 B/op 0 allocs/op BenchmarkParse/canada/stdjson-map 20 68147307 ns/op 33.03 MB/s 12260502 B/op 392539 allocs/op BenchmarkParse/canada/stdjson-struct 20 68044518 ns/op 33.08 MB/s 12260123 B/op 392534 allocs/op BenchmarkParse/canada/stdjson-empty-struct 100 17709250 ns/op 127.11 MB/s 280 B/op 5 allocs/op BenchmarkParse/canada/fastjson 300 4182404 ns/op 538.22 MB/s 254902 B/op 381 allocs/op BenchmarkParse/canada/fastjson-get 300 4274744 ns/op 526.60 MB/s 254902 B/op 381 allocs/op BenchmarkParse/citm/stdjson-map 50 27772612 ns/op 62.19 MB/s 5214163 B/op 95402 allocs/op BenchmarkParse/citm/stdjson-struct 100 14936191 ns/op 115.64 MB/s 1989 B/op 75 allocs/op BenchmarkParse/citm/stdjson-empty-struct 100 14946034 ns/op 115.56 MB/s 280 B/op 5 allocs/op BenchmarkParse/citm/fastjson 1000 1879714 ns/op 918.87 MB/s 17628 B/op 30 allocs/op BenchmarkParse/citm/fastjson-get 1000 1881598 ns/op 917.94 MB/s 17628 B/op 30 allocs/op BenchmarkParse/twitter/stdjson-map 100 11289146 ns/op 55.94 MB/s 2187878 B/op 31266 allocs/op BenchmarkParse/twitter/stdjson-struct 300 5779442 ns/op 109.27 MB/s 408 B/op 6 allocs/op BenchmarkParse/twitter/stdjson-empty-struct 300 5738504 ns/op 110.05 MB/s 408 B/op 6 allocs/op BenchmarkParse/twitter/fastjson 2000 774042 ns/op 815.86 MB/s 2541 B/op 2 allocs/op BenchmarkParse/twitter/fastjson-get 2000 777833 ns/op 811.89 MB/s 2541 B/op 2 allocs/op ``` JSON 验证的基准测试结果: ``` $ GOMAXPROCS=1 go test github.com/valyala/fastjson -bench='Validate$' goos: linux goarch: amd64 pkg: github.com/valyala/fastjson BenchmarkValidate/small/stdjson 2000000 955 ns/op 198.83 MB/s 72 B/op 2 allocs/op BenchmarkValidate/small/fastjson 5000000 384 ns/op 493.60 MB/s 0 B/op 0 allocs/op BenchmarkValidate/medium/stdjson 200000 10799 ns/op 215.66 MB/s 184 B/op 5 allocs/op BenchmarkValidate/medium/fastjson 300000 3809 ns/op 611.30 MB/s 0 B/op 0 allocs/op BenchmarkValidate/large/stdjson 10000 133064 ns/op 211.31 MB/s 184 B/op 5 allocs/op BenchmarkValidate/large/fastjson 30000 45268 ns/op 621.14 MB/s 0 B/op 0 allocs/op BenchmarkValidate/canada/stdjson 200 8470904 ns/op 265.74 MB/s 184 B/op 5 allocs/op BenchmarkValidate/canada/fastjson 500 2973377 ns/op 757.07 MB/s 0 B/op 0 allocs/op BenchmarkValidate/citm/stdjson 200 7273172 ns/op 237.48 MB/s 184 B/op 5 allocs/op BenchmarkValidate/citm/fastjson 1000 1684430 ns/op 1025.39 MB/s 0 B/op 0 allocs/op BenchmarkValidate/twitter/stdjson 500 2849439 ns/op 221.63 MB/s 312 B/op 6 allocs/op BenchmarkValidate/twitter/fastjson 2000 1036796 ns/op 609.10 MB/s 0 B/op 0 allocs/op ``` ## 常见问题 * 问:_Go 中已经有大量高性能的 JSON 解析包。为什么还要创建另一个包?_ 答:因为其他包要么需要通过 struct magic 和代码生成来指定严格的 JSON schema, 要么在必须从解析的 JSON 中获取多个不相关的字段时性能较差。 此外,`fastjson` 提供了更好的 [API](http://godoc.org/github.com/valyala/fastjson)。 * 问:_`fastjson` 的主要目的是什么?_ 答:为 [RTB](https://www.iab.com/wp-content/uploads/2015/05/OpenRTB_API_Specification_Version_2_3_1.pdf) 和其他 [JSON-RPC](https://en.wikipedia.org/wiki/JSON-RPC) 服务提供高性能的 JSON 解析。 * 问:_为什么 fastjson 不提供快速的 marshaling(序列化)?_ 答:实际上,它提供了某种形式的 marshaling —— 参见 [Value.MarshalTo](https://godoc.org/github.com/valyala/fastjson#Value.MarshalTo)。 但我建议使用 [quicktemplate](https://github.com/valyala/quicktemplate#use-cases) 来进行高性能的 JSON marshaling :) * 问:_`fastjson` 导致我的程序崩溃!_ 答:很有可能是使用不当。 * 确保如果 [docs](https://github.com/valyala/fastjson/issues/new) 中提到了相关限制,你不要在下一次 `Parser.Parse` / `Scanner.Next` 调用之后, 依然持有 `Parser` / `Scanner` 递归返回的对象的引用。 * 确保如果 [docs](https://github.com/valyala/fastjson/issues/new) 中提到了相关限制,你不要从并发运行的 goroutine 中访问 `fastjson` 对象。 * 使用 [-race](https://golang.org/doc/articles/race_detector.html) 标志构建并运行你的程序。 确保竞态检测器检测到的竞态为零。 * 如果在修复了上述问题后程序仍然崩溃,请[提交 bug](https://github.com/valyala/fastjson/issues/new)。
标签:EVTX分析, Go, JSON解析器, Ruby工具, 开发库, 日志审计, 解析与验证