الإنجليزية | 中文
مكتبة تسلسل وإلغاء تسلسل 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 (انظر Escape HTML) وميزة SortKeys
(الدعم الاختياري، راجع Sort Keys) التي لا تتوافق مع RFC8259.
import "github.com/bytedance/sonic"
var data YourSchema
// Marshal
output , err := sonic . Marshal ( & data )
// Unmarshal
err := sonic . Unmarshal ( output , & data )
تدعم تقنية Sonic فك تشفير json من io.Reader
أو تشفير الكائنات إلى 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 هو AST مستقل تمامًا لـ JSON. يقوم بتنفيذ التسلسل وإلغاء التسلسل ويوفر واجهات برمجة التطبيقات القوية للحصول على البيانات العامة وتعديلها.
ابحث عن JSON جزئيًا من خلال مسارات معينة، والتي يجب أن تكون عددًا صحيحًا أو سلسلة غير سالبة، أو صفرًا
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
، فهو لا يدعم القراءة المتزامنة افتراضيًا. إذا كنت ترغب في قراءتها في وقت واحد، يرجى تحديد ذلك.قم بتعديل محتوى json بواسطة 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"
لتشفير 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 واجهة برمجة تطبيقات متقدمة لتحليل 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 واجهة برمجة التطبيقات التالية لإرجاع اجتياز الطلب المسبق لـ 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 للاطلاع على الاستخدام التفصيلي. نقوم أيضًا بتنفيذ زائر تجريبي لـ UserNode
في ast/visitor_test.go.
بالنسبة للمطورين الذين يرغبون في استخدام Sonic لتلبية سيناريوهات مختلفة، فإننا نقدم بعض التكوينات المتكاملة مثل sonic.API
ConfigDefault
: التكوين الافتراضي للصوت ( EscapeHTML=false
، SortKeys=false
...) لتشغيل الصوت بسرعة مع ضمان الأمان.ConfigStd
: التكوين المتوافق مع std ( EscapeHTML=true
, SortKeys=true
...)ConfigFastest
: أسرع تكوين ( NoQuoteTextMarshaler=true
) للتشغيل على الصوت بأسرع ما يمكن. لا تضمن Sonic دعم جميع البيئات، وذلك بسبب صعوبة تطوير رموز عالية الأداء. في البيئة غير الداعمة للصوت، سيعود التنفيذ إلى encoding/json
. وبالتالي فإن جميع التكوينات المتدفقة ستكون مساوية لـ ConfigStd
. نظرًا لأن Sonic يستخدم golang-asm كمجمع JIT، وهو ليس مناسبًا جدًا للتجميع في وقت التشغيل، فإن التشغيل الأول لمخطط ضخم قد يتسبب في انتهاء مهلة الطلب أو حتى عملية OOM. للحصول على استقرار أفضل، ننصح باستخدام Pretouch()
لتطبيقات المخططات الضخمة أو الذاكرة المدمجة قبل 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