英语 | 中文
一个极快的 JSON 序列化和反序列化库,通过 JIT(即时编译)和 SIMD(单指令多数据)加速。
参见 go.dev
对于所有大小的 json 和所有使用场景, Sonic 表现最好。
goversion: 1.17 . 1
goos: darwin
goarch: amd64
cpu: Intel(R) Core(TM) i9 - 9880H CPU @ 2. 30GHz
BenchmarkEncoder_Generic_Sonic - 16 32393 ns / op 402.40 MB / s 11965 B / op 4 allocs / op
BenchmarkEncoder_Generic_Sonic_Fast - 16 21668 ns / op 601.57 MB / s 10940 B / op 4 allocs / op
BenchmarkEncoder_Generic_JsonIter - 16 42168 ns / op 309.12 MB / s 14345 B / op 115 allocs / op
BenchmarkEncoder_Generic_GoJson - 16 65189 ns / op 199.96 MB / s 23261 B / op 16 allocs / op
BenchmarkEncoder_Generic_StdLib - 16 106322 ns / op 122.60 MB / s 49136 B / op 789 allocs / op
BenchmarkEncoder_Binding_Sonic - 16 6269 ns / op 2079.26 MB / s 14173 B / op 4 allocs / op
BenchmarkEncoder_Binding_Sonic_Fast - 16 5281 ns / op 2468.16 MB / s 12322 B / op 4 allocs / op
BenchmarkEncoder_Binding_JsonIter - 16 20056 ns / op 649.93 MB / s 9488 B / op 2 allocs / op
BenchmarkEncoder_Binding_GoJson - 16 8311 ns / op 1568.32 MB / s 9481 B / op 1 allocs / op
BenchmarkEncoder_Binding_StdLib - 16 16448 ns / op 792.52 MB / s 9479 B / op 1 allocs / op
BenchmarkEncoder_Parallel_Generic_Sonic - 16 6681 ns / op 1950.93 MB / s 12738 B / op 4 allocs / op
BenchmarkEncoder_Parallel_Generic_Sonic_Fast - 16 4179 ns / op 3118.99 MB / s 10757 B / op 4 allocs / op
BenchmarkEncoder_Parallel_Generic_JsonIter - 16 9861 ns / op 1321.84 MB / s 14362 B / op 115 allocs / op
BenchmarkEncoder_Parallel_Generic_GoJson - 16 18850 ns / op 691.52 MB / s 23278 B / op 16 allocs / op
BenchmarkEncoder_Parallel_Generic_StdLib - 16 45902 ns / op 283.97 MB / s 49174 B / op 789 allocs / op
BenchmarkEncoder_Parallel_Binding_Sonic - 16 1480 ns / op 8810.09 MB / s 13049 B / op 4 allocs / op
BenchmarkEncoder_Parallel_Binding_Sonic_Fast - 16 1209 ns / op 10785.23 MB / s 11546 B / op 4 allocs / op
BenchmarkEncoder_Parallel_Binding_JsonIter - 16 6170 ns / op 2112.58 MB / s 9504 B / op 2 allocs / op
BenchmarkEncoder_Parallel_Binding_GoJson - 16 3321 ns / op 3925.52 MB / s 9496 B / op 1 allocs / op
BenchmarkEncoder_Parallel_Binding_StdLib - 16 3739 ns / op 3486.49 MB / s 9480 B / op 1 allocs / op
BenchmarkDecoder_Generic_Sonic - 16 66812 ns / op 195.10 MB / s 57602 B / op 723 allocs / op
BenchmarkDecoder_Generic_Sonic_Fast - 16 54523 ns / op 239.07 MB / s 49786 B / op 313 allocs / op
BenchmarkDecoder_Generic_StdLib - 16 124260 ns / op 104.90 MB / s 50869 B / op 772 allocs / op
BenchmarkDecoder_Generic_JsonIter - 16 91274 ns / op 142.81 MB / s 55782 B / op 1068 allocs / op
BenchmarkDecoder_Generic_GoJson - 16 88569 ns / op 147.17 MB / s 66367 B / op 973 allocs / op
BenchmarkDecoder_Binding_Sonic - 16 32557 ns / op 400.38 MB / s 28302 B / op 137 allocs / op
BenchmarkDecoder_Binding_Sonic_Fast - 16 28649 ns / op 455.00 MB / s 24999 B / op 34 allocs / op
BenchmarkDecoder_Binding_StdLib - 16 111437 ns / op 116.97 MB / s 10576 B / op 208 allocs / op
BenchmarkDecoder_Binding_JsonIter - 16 35090 ns / op 371.48 MB / s 14673 B / op 385 allocs / op
BenchmarkDecoder_Binding_GoJson - 16 28738 ns / op 453.59 MB / s 22039 B / op 49 allocs / op
BenchmarkDecoder_Parallel_Generic_Sonic - 16 12321 ns / op 1057.91 MB / s 57233 B / op 723 allocs / op
BenchmarkDecoder_Parallel_Generic_Sonic_Fast - 16 10644 ns / op 1224.64 MB / s 49362 B / op 313 allocs / op
BenchmarkDecoder_Parallel_Generic_StdLib - 16 57587 ns / op 226.35 MB / s 50874 B / op 772 allocs / op
BenchmarkDecoder_Parallel_Generic_JsonIter - 16 38666 ns / op 337.12 MB / s 55789 B / op 1068 allocs / op
BenchmarkDecoder_Parallel_Generic_GoJson - 16 30259 ns / op 430.79 MB / s 66370 B / op 974 allocs / op
BenchmarkDecoder_Parallel_Binding_Sonic - 16 5965 ns / op 2185.28 MB / s 27747 B / op 137 allocs / op
BenchmarkDecoder_Parallel_Binding_Sonic_Fast - 16 5170 ns / op 2521.31 MB / s 24715 B / op 34 allocs / op
BenchmarkDecoder_Parallel_Binding_StdLib - 16 27582 ns / op 472.58 MB / s 10576 B / op 208 allocs / op
BenchmarkDecoder_Parallel_Binding_JsonIter - 16 13571 ns / op 960.51 MB / s 14685 B / op 385 allocs / op
BenchmarkDecoder_Parallel_Binding_GoJson - 16 10031 ns / op 1299.51 MB / s 22111 B / op 49 allocs / op
BenchmarkGetOne_Sonic - 16 3276 ns / op 3975.78 MB / s 24 B / op 1 allocs / op
BenchmarkGetOne_Gjson - 16 9431 ns / op 1380.81 MB / s 0 B / op 0 allocs / op
BenchmarkGetOne_Jsoniter - 16 51178 ns / op 254.46 MB / s 27936 B / op 647 allocs / op
BenchmarkGetOne_Parallel_Sonic - 16 216.7 ns / op 60098.95 MB / s 24 B / op 1 allocs / op
BenchmarkGetOne_Parallel_Gjson - 16 1076 ns / op 12098.62 MB / s 0 B / op 0 allocs / op
BenchmarkGetOne_Parallel_Jsoniter - 16 17741 ns / op 734.06 MB / s 27945 B / op 647 allocs / op
BenchmarkSetOne_Sonic - 16 9571 ns / op 1360.61 MB / s 1584 B / op 17 allocs / op
BenchmarkSetOne_Sjson - 16 36456 ns / op 357.22 MB / s 52180 B / op 9 allocs / op
BenchmarkSetOne_Jsoniter - 16 79475 ns / op 163.86 MB / s 45862 B / op 964 allocs / op
BenchmarkSetOne_Parallel_Sonic - 16 850.9 ns / op 15305.31 MB / s 1584 B / op 17 allocs / op
BenchmarkSetOne_Parallel_Sjson - 16 18194 ns / op 715.77 MB / s 52247 B / op 9 allocs / op
BenchmarkSetOne_Parallel_Jsoniter - 16 33560 ns / op 388.05 MB / s 45892 B / op 964 allocs / op
BenchmarkLoadNode / LoadAll() -16 11384 ns / op 1143.93 MB / s 6307 B / op 25 allocs / op
BenchmarkLoadNode_Parallel / LoadAll() -16 5493 ns / op 2370.68 MB / s 7145 B / op 25 allocs / op
BenchmarkLoadNode / Interface() -16 17722 ns / op 734.85 MB / s 13323 B / op 88 allocs / op
BenchmarkLoadNode_Parallel / Interface() -16 10330 ns / op 1260.70 MB / s 15178 B / op 88 allocs / op
请参阅 bench.sh 了解基准代码。
请参阅 INTRODUCTION.md。
默认行为大多与encoding/json
一致,除了HTML转义表单(请参阅转义HTML)和SortKeys
功能(可选支持请参阅排序键)不符合RFC8259。
import "github.com/bytedance/sonic"
var data YourSchema
// Marshal
output , err := sonic . Marshal ( & data )
// Unmarshal
err := sonic . Unmarshal ( output , & data )
Sonic 支持从io.Reader
解码 json 或将对象编码为io.Writer
,旨在处理多个值并减少内存消耗。
var o1 = map [ string ] interface {}{
"a" : "b" ,
}
var o2 = 1
var w = bytes . NewBuffer ( nil )
var enc = sonic . ConfigDefault . NewEncoder ( w )
enc . Encode ( o1 )
enc . Encode ( o2 )
fmt . Println ( w . String ())
// Output:
// {"a":"b"}
// 1
var o = map [ string ] interface {}{}
var r = strings . NewReader ( `{"a":"b"}{"1":"2"}` )
var dec = sonic . ConfigDefault . NewDecoder ( r )
dec . Decode ( & o )
dec . Decode ( & o )
fmt . Printf ( "%+v" , o )
// Output:
// map[1:2 a:b]
import "github.com/bytedance/sonic/decoder"
var input = `1`
var data interface {}
// default float64
dc := decoder . NewDecoder ( input )
dc . Decode ( & data ) // data == float64(1)
// use json.Number
dc = decoder . NewDecoder ( input )
dc . UseNumber ()
dc . Decode ( & data ) // data == json.Number("1")
// use int64
dc = decoder . NewDecoder ( input )
dc . UseInt64 ()
dc . Decode ( & data ) // data == int64(1)
root , err := sonic . GetFromString ( input )
// Get json.Number
jn := root . Number ()
jm := root . InterfaceUseNumber ().(json. Number ) // jn == jm
// Get float64
fn := root . Float64 ()
fm := root . Interface ().( float64 ) // jn == jm
考虑到排序带来的性能损失(大约 10%),sonic 默认情况下不启用此功能。如果您的组件依赖于它来工作(例如 zstd),请像这样使用它:
import "github.com/bytedance/sonic"
import "github.com/bytedance/sonic/encoder"
// Binding map only
m := map [ string ] interface {}{}
v , err := encoder . Encode ( m , encoder . SortMapKeys )
// Or ast.Node.SortKeys() before marshal
var root := sonic. Get ( JSON )
err := root . SortKeys ()
考虑到性能损失(大约15%),sonic默认不启用此功能。您可以使用encoder.EscapeHTML
选项来打开此功能(与encoding/json.HTMLEscape
对齐)。
import "github.com/bytedance/sonic"
v := map [ string ] string { "&&" : "<>" }
ret , err := Encode ( v , EscapeHTML ) // ret == `{"u0026u0026":{"X":"u003cu003e"}}`
默认情况下,Sonic 将原始对象(struct/map...)编码为紧凑格式 JSON,除了编组json.RawMessage
或json.Marshaler
:sonic 确保验证其输出 JSON,但出于性能考虑不压缩它们。我们提供选项encoder.CompactMarshaler
来添加压缩过程。
如果输入 JSON 中存在无效语法,sonic 将返回decoder.SyntaxError
,它支持错误位置的漂亮打印
import "github.com/bytedance/sonic"
import "github.com/bytedance/sonic/decoder"
var data interface {}
err := sonic . UnmarshalString ( "[[[}]]" , & data )
if err != nil {
/* One line by default */
println ( e . Error ()) // "Syntax error at index 3: invalid charnnt[[[}]]nt...^..n"
/* Pretty print */
if e , ok := err .(decoder. SyntaxError ); ok {
/*Syntax error at index 3: invalid char
[[[}]]
...^..
*/
print ( e . Description ())
} else if me , ok := err .( * decoder. MismatchTypeError ); ok {
// decoder.MismatchTypeError is new to Sonic v1.6.0
print ( me . Description ())
}
}
如果给定的键存在不匹配类型的值,sonic将报告decoder.MismatchTypeError
(如果有很多,则报告最后一个),但仍然跳过错误的值并继续解码下一个JSON。
import "github.com/bytedance/sonic"
import "github.com/bytedance/sonic/decoder"
var data = struct {
A int
B int
}{}
err := UnmarshalString ( `{"A":"1","B":1}` , & data )
println ( err . Error ()) // Mismatch type int with value string "at index 5: mismatched type with valuennt{"A":"1","B":1}nt.....^.........n"
fmt . Printf ( "%+v" , data ) // {A:0 B:1}
Sonic/ast.Node 是一个完全独立的 JSON AST。它实现了序列化和反序列化,并提供了用于获取和修改通用数据的强大 API。
通过给定路径搜索部分 JSON,该路径必须是非负整数或字符串,或者 nil
import "github.com/bytedance/sonic"
input := [] byte ( `{"key1":[{},{"key2":{"key3":[1,2,3]}}]}` )
// no path, returns entire json
root , err := sonic . Get ( input )
raw := root . Raw () // == string(input)
// multiple paths
root , err := sonic . Get ( input , "key1" , 1 , "key2" )
sub := root . Get ( "key3" ). Index ( 2 ). Int64 () // == 3
提示:由于Index()
使用偏移量来定位数据,这比Get()
等扫描要快得多,因此我们建议您尽可能使用它。 sonic 还提供了另一个 API IndexOrGet()
来底层使用偏移量并确保密钥匹配。
Searcher
为用户提供了一些选项来满足不同的需求:
opts := ast. SearchOption { CopyReturn : true ... }
val , err := sonic . GetWithOptions ( JSON , opts , "key" )
ast.Node
采用Lazy-Load
设计,所以默认不支持Concurrently-Read。如果要同时阅读,请注明。通过Set()/Unset()修改json内容
import "github.com/bytedance/sonic"
// Set
exist , err := root . Set ( "key4" , NewBool ( true )) // exist == false
alias1 := root . Get ( "key4" )
println ( alias1 . Valid ()) // true
alias2 := root . Index ( 1 )
println ( alias1 == alias2 ) // true
// Unset
exist , err := root . UnsetByIndex ( 1 ) // exist == true
println ( root . Get ( "key4" ). Check ()) // "value not exist"
要将ast.Node
编码为 json,请使用MarshalJson()
或json.Marshal()
(必须传递节点的指针)
import (
"encoding/json"
"github.com/bytedance/sonic"
)
buf , err := root . MarshalJson ()
println ( string ( buf )) // {"key1":[{},{"key2":{"key3":[1,2,3]}}]}
exp , err := json . Marshal ( & root ) // WARN: use pointer
println ( string ( buf ) == string ( exp )) // true
Check()
、 Error()
、 Valid()
、 Exist()
Index()
、 Get()
、 IndexPair()
、 IndexOrGet()
、 GetByPath()
Int64()
、 Float64()
、 String()
、 Number()
、 Bool()
、 Map[UseNumber|UseNode]()
、 Array[UseNumber|UseNode]()
、 Interface[UseNumber|UseNode]()
NewRaw()
、 NewNumber()
、 NewNull()
、 NewBool()
、 NewString()
、 NewObject()
、 NewArray()
Values()
、 Properties()
、 ForEach()
、 SortKeys()
Set()
、 SetByIndex()
、 Add()
Sonic 提供了一个高级 API,用于将 JSON 完全解析为非标准类型(无论是struct
还是map[string]interface{}
),而无需使用任何中间表示( ast.Node
或interface{}
)。例如,您可能有以下类似于interface{}
但实际上不是interface{}
类型:
type UserNode interface {}
// the following types implement the UserNode interface.
type (
UserNull struct {}
UserBool struct { Value bool }
UserInt64 struct { Value int64 }
UserFloat64 struct { Value float64 }
UserString struct { Value string }
UserObject struct { Value map [ string ] UserNode }
UserArray struct { Value [] UserNode }
)
Sonic 提供以下 API 来返回JSON AST 的前序遍历。 ast.Visitor
是一个 SAX 风格的接口,在一些 C++ JSON 库中使用。您应该自己实现ast.Visitor
并将其传递给ast.Preorder()
方法。在访问者中,您可以创建自定义类型来表示 JSON 值。您的访问者中可能有一个 O(n) 空间容器(例如堆栈)来记录对象/数组层次结构。
func Preorder ( str string , visitor Visitor , opts * VisitorOptions ) error
type Visitor interface {
OnNull () error
OnBool ( v bool ) error
OnString ( v string ) error
OnInt64 ( v int64 , n json. Number ) error
OnFloat64 ( v float64 , n json. Number ) error
OnObjectBegin ( capacity int ) error
OnObjectKey ( key string ) error
OnObjectEnd () error
OnArrayBegin ( capacity int ) error
OnArrayEnd () error
}
详细使用方法请参见ast/visitor.go。我们还在 ast/visitor_test.go 中为UserNode
实现了一个演示访问者。
对于想要使用 sonic 来满足不同场景的开发者,我们提供了一些集成配置,如sonic.API
ConfigDefault
: sonic 的默认配置 ( EscapeHTML=false
, SortKeys=false
...) 可以快速运行 sonic 同时确保安全。ConfigStd
:std 兼容的配置( EscapeHTML=true
, SortKeys=true
...)ConfigFastest
:在 sonic 上尽可能快地运行的最快配置 ( NoQuoteTextMarshaler=true
)。由于开发高性能代码的难度,Sonic不确保支持所有环境。在不支持声音的环境中,实现将回退到encoding/json
。因此,下面的配置将全部等于ConfigStd
。 由于 Sonic 使用 golang-asm 作为 JIT 汇编器,不太适合运行时编译,因此第一次运行大型 schema 可能会导致请求超时甚至进程 OOM。为了获得更好的稳定性,我们建议在Marshal()/Unmarshal()
之前对大型模式或紧凑内存应用程序使用Pretouch()
。
import (
"reflect"
"github.com/bytedance/sonic"
"github.com/bytedance/sonic/option"
)
func init () {
var v HugeStruct
// For most large types (nesting depth <= option.DefaultMaxIn