英語 | 中文
一個極快的 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