Inglês | 中文
Uma biblioteca de serialização e desserialização JSON extremamente rápida, acelerada por JIT (compilação just-in-time) e SIMD (instrução única-múltiplos dados).
veja go.dev
Para todos os tamanhos de json e todos os cenários de uso, o Sonic tem melhor desempenho .
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
Consulte bench.sh para códigos de benchmark.
Consulte INTRODUÇÃO.md.
Os comportamentos padrão são principalmente consistentes com encoding/json
, exceto o formulário de escape HTML (consulte Escape HTML) e o recurso SortKeys
(suporte opcional, consulte Sort Keys) que NÃO está em conformidade com RFC8259.
import "github.com/bytedance/sonic"
var data YourSchema
// Marshal
output , err := sonic . Marshal ( & data )
// Unmarshal
err := sonic . Unmarshal ( output , & data )
Sonic suporta a decodificação de json de io.Reader
ou a codificação de objetos em io.Writer
, visando lidar com vários valores e também reduzir o consumo de memória.
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
Devido à perda de desempenho na classificação (cerca de 10%), o Sonic não habilita esse recurso por padrão. Se o seu componente depende dele para funcionar (como o zstd), use-o assim:
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 ()
Por conta da perda de desempenho (cerca de 15%), o Sonic não habilita esse recurso por padrão. Você pode usar a opção encoder.EscapeHTML
para abrir este recurso (alinhar com encoding/json.HTMLEscape
).
import "github.com/bytedance/sonic"
v := map [ string ] string { "&&" : "<>" }
ret , err := Encode ( v , EscapeHTML ) // ret == `{"u0026u0026":{"X":"u003cu003e"}}`
Sonic codifica objetos primitivos (struct/map...) como JSON de formato compacto por padrão, exceto empacotamento json.RawMessage
ou json.Marshaler
: sonic garante a validação de seu JSON de saída, mas NÃO os compacta por questões de desempenho. Fornecemos a opção encoder.CompactMarshaler
para adicionar processo de compactação.
Se houver sintaxe inválida no JSON de entrada, sonic retornará decoder.SyntaxError
, que suporta impressão bonita da posição do erro
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 ())
}
}
Se houver um valor digitado incompatível para uma determinada chave, o Sonic reportará decoder.MismatchTypeError
(se houver muitos, reportará o último), mas ainda assim pulará o valor errado e continuará decodificando o próximo 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 é um AST totalmente independente para JSON. Ele implementa serialização e desserialização e fornece APIs robustas para obtenção e modificação de dados genéricos.
Pesquise JSON parcial por determinados caminhos, que devem ser inteiros ou strings não negativos, ou nulos
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
Dica : como Index()
usa offset para localizar dados, o que é muito mais rápido do que uma varredura como Get()
, sugerimos que você use-o tanto quanto possível. E o Sonic também fornece outra API IndexOrGet()
para o deslocamento de uso subjacente, bem como para garantir que a chave seja correspondida.
Searcher
oferece algumas opções para o usuário atender a diferentes necessidades:
opts := ast. SearchOption { CopyReturn : true ... }
val , err := sonic . GetWithOptions ( JSON , opts , "key" )
ast.Node
usa o design Lazy-Load
, ele não suporta leitura simultânea por padrão. Se você quiser lê-lo simultaneamente, especifique-o.Modifique o conteúdo json por Set()/Unset()
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"
Para codificar ast.Node
como json, use MarshalJson()
ou json.Marshal()
(DEVE passar o ponteiro do nó)
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 fornece uma API avançada para analisar completamente JSON em tipos não padrão (nem struct
nem map[string]interface{}
) sem usar qualquer representação intermediária ( ast.Node
ou interface{}
). Por exemplo, você pode ter os seguintes tipos que são como interface{}
mas na verdade não são 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 fornece a seguinte API para retornar a passagem de pré-pedido de um JSON AST . O ast.Visitor
é uma interface estilo SAX que é usada em algumas bibliotecas C++ JSON. Você deve implementar ast.Visitor
sozinho e passá-lo para o método ast.Preorder()
. No seu visitante você pode criar seus tipos personalizados para representar valores JSON. Pode haver um contêiner de espaço O(n) (como pilha) em seu visitante para registrar a hierarquia de objeto/matriz.
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
}
Consulte ast/visitor.go para uso detalhado. Também implementamos um visitante de demonstração para UserNode
em ast/visitor_test.go.
Para desenvolvedores que desejam usar o Sonic para atender a diferentes cenários, fornecemos algumas configurações integradas como sonic.API
ConfigDefault
: a configuração padrão do Sonic ( EscapeHTML=false
, SortKeys=false
...) para executar o Sonic rapidamente enquanto garante a segurança.ConfigStd
: a configuração compatível com std ( EscapeHTML=true
, SortKeys=true
...)ConfigFastest
: a configuração mais rápida ( NoQuoteTextMarshaler=true
) para rodar no Sonic o mais rápido possível. A Sonic NÃO garante suporte a todos os ambientes, devido à dificuldade de desenvolvimento de códigos de alto desempenho. Em um ambiente sem suporte sônico, a implementação retornará para encoding/json
. Assim, as configurações do beflow serão todas iguais a ConfigStd
. Como o Sonic usa golang-asm como um montador JIT, que NÃO é muito adequado para compilação em tempo de execução, a execução inicial de um esquema enorme pode causar tempo limite de solicitação ou até mesmo OOM de processo. Para melhor estabilidade, recomendamos usar Pretouch()
para aplicativos de esquema enorme ou memória compacta antes de Marshal()/Unmarshal()
.
import (
"reflect"
"github.com/bytedance/sonic"
"github.com/bytedance/sonic/option"
)
func init () {
var v HugeStruct
// For most large types (nesting depth <= option.DefaultMaxIn