github.com/goshafaq/sonic@v0.0.0-20231026082336-871835fb94c6/README_ZH_CN.md (about) 1 # Sonic 2 3 [English](README.md) | 中文 4 5 一个速度奇快的 JSON 序列化/反序列化库,由 JIT (即时编译)和 SIMD (单指令流多数据流)加速。 6 7 ## 依赖 8 9 - Go 1.16~1.21 10 - Linux / MacOS / Windows(需要 Go1.17 以上) 11 - Amd64 架构 12 13 ## 特色 14 15 - 运行时对象绑定,无需代码生成 16 - 完备的 JSON 操作 API 17 - 快,更快,还要更快! 18 19 ## 基准测试 20 21 对于**所有大小**的 json 和**所有使用场景**, **Sonic 表现均为最佳**。 22 - [中型](https://github.com/goshafaq/sonic/blob/main/decoder/testdata_test.go#L19) (13kB, 300+ 键, 6 层) 23 ```powershell 24 goversion: 1.17.1 25 goos: darwin 26 goarch: amd64 27 cpu: Intel(R) Core(TM) i9-9880H CPU @ 2.30GHz 28 BenchmarkEncoder_Generic_Sonic-16 32393 ns/op 402.40 MB/s 11965 B/op 4 allocs/op 29 BenchmarkEncoder_Generic_Sonic_Fast-16 21668 ns/op 601.57 MB/s 10940 B/op 4 allocs/op 30 BenchmarkEncoder_Generic_JsonIter-16 42168 ns/op 309.12 MB/s 14345 B/op 115 allocs/op 31 BenchmarkEncoder_Generic_GoJson-16 65189 ns/op 199.96 MB/s 23261 B/op 16 allocs/op 32 BenchmarkEncoder_Generic_StdLib-16 106322 ns/op 122.60 MB/s 49136 B/op 789 allocs/op 33 BenchmarkEncoder_Binding_Sonic-16 6269 ns/op 2079.26 MB/s 14173 B/op 4 allocs/op 34 BenchmarkEncoder_Binding_Sonic_Fast-16 5281 ns/op 2468.16 MB/s 12322 B/op 4 allocs/op 35 BenchmarkEncoder_Binding_JsonIter-16 20056 ns/op 649.93 MB/s 9488 B/op 2 allocs/op 36 BenchmarkEncoder_Binding_GoJson-16 8311 ns/op 1568.32 MB/s 9481 B/op 1 allocs/op 37 BenchmarkEncoder_Binding_StdLib-16 16448 ns/op 792.52 MB/s 9479 B/op 1 allocs/op 38 BenchmarkEncoder_Parallel_Generic_Sonic-16 6681 ns/op 1950.93 MB/s 12738 B/op 4 allocs/op 39 BenchmarkEncoder_Parallel_Generic_Sonic_Fast-16 4179 ns/op 3118.99 MB/s 10757 B/op 4 allocs/op 40 BenchmarkEncoder_Parallel_Generic_JsonIter-16 9861 ns/op 1321.84 MB/s 14362 B/op 115 allocs/op 41 BenchmarkEncoder_Parallel_Generic_GoJson-16 18850 ns/op 691.52 MB/s 23278 B/op 16 allocs/op 42 BenchmarkEncoder_Parallel_Generic_StdLib-16 45902 ns/op 283.97 MB/s 49174 B/op 789 allocs/op 43 BenchmarkEncoder_Parallel_Binding_Sonic-16 1480 ns/op 8810.09 MB/s 13049 B/op 4 allocs/op 44 BenchmarkEncoder_Parallel_Binding_Sonic_Fast-16 1209 ns/op 10785.23 MB/s 11546 B/op 4 allocs/op 45 BenchmarkEncoder_Parallel_Binding_JsonIter-16 6170 ns/op 2112.58 MB/s 9504 B/op 2 allocs/op 46 BenchmarkEncoder_Parallel_Binding_GoJson-16 3321 ns/op 3925.52 MB/s 9496 B/op 1 allocs/op 47 BenchmarkEncoder_Parallel_Binding_StdLib-16 3739 ns/op 3486.49 MB/s 9480 B/op 1 allocs/op 48 49 BenchmarkDecoder_Generic_Sonic-16 66812 ns/op 195.10 MB/s 57602 B/op 723 allocs/op 50 BenchmarkDecoder_Generic_Sonic_Fast-16 54523 ns/op 239.07 MB/s 49786 B/op 313 allocs/op 51 BenchmarkDecoder_Generic_StdLib-16 124260 ns/op 104.90 MB/s 50869 B/op 772 allocs/op 52 BenchmarkDecoder_Generic_JsonIter-16 91274 ns/op 142.81 MB/s 55782 B/op 1068 allocs/op 53 BenchmarkDecoder_Generic_GoJson-16 88569 ns/op 147.17 MB/s 66367 B/op 973 allocs/op 54 BenchmarkDecoder_Binding_Sonic-16 32557 ns/op 400.38 MB/s 28302 B/op 137 allocs/op 55 BenchmarkDecoder_Binding_Sonic_Fast-16 28649 ns/op 455.00 MB/s 24999 B/op 34 allocs/op 56 BenchmarkDecoder_Binding_StdLib-16 111437 ns/op 116.97 MB/s 10576 B/op 208 allocs/op 57 BenchmarkDecoder_Binding_JsonIter-16 35090 ns/op 371.48 MB/s 14673 B/op 385 allocs/op 58 BenchmarkDecoder_Binding_GoJson-16 28738 ns/op 453.59 MB/s 22039 B/op 49 allocs/op 59 BenchmarkDecoder_Parallel_Generic_Sonic-16 12321 ns/op 1057.91 MB/s 57233 B/op 723 allocs/op 60 BenchmarkDecoder_Parallel_Generic_Sonic_Fast-16 10644 ns/op 1224.64 MB/s 49362 B/op 313 allocs/op 61 BenchmarkDecoder_Parallel_Generic_StdLib-16 57587 ns/op 226.35 MB/s 50874 B/op 772 allocs/op 62 BenchmarkDecoder_Parallel_Generic_JsonIter-16 38666 ns/op 337.12 MB/s 55789 B/op 1068 allocs/op 63 BenchmarkDecoder_Parallel_Generic_GoJson-16 30259 ns/op 430.79 MB/s 66370 B/op 974 allocs/op 64 BenchmarkDecoder_Parallel_Binding_Sonic-16 5965 ns/op 2185.28 MB/s 27747 B/op 137 allocs/op 65 BenchmarkDecoder_Parallel_Binding_Sonic_Fast-16 5170 ns/op 2521.31 MB/s 24715 B/op 34 allocs/op 66 BenchmarkDecoder_Parallel_Binding_StdLib-16 27582 ns/op 472.58 MB/s 10576 B/op 208 allocs/op 67 BenchmarkDecoder_Parallel_Binding_JsonIter-16 13571 ns/op 960.51 MB/s 14685 B/op 385 allocs/op 68 BenchmarkDecoder_Parallel_Binding_GoJson-16 10031 ns/op 1299.51 MB/s 22111 B/op 49 allocs/op 69 70 BenchmarkGetOne_Sonic-16 3276 ns/op 3975.78 MB/s 24 B/op 1 allocs/op 71 BenchmarkGetOne_Gjson-16 9431 ns/op 1380.81 MB/s 0 B/op 0 allocs/op 72 BenchmarkGetOne_Jsoniter-16 51178 ns/op 254.46 MB/s 27936 B/op 647 allocs/op 73 BenchmarkGetOne_Parallel_Sonic-16 216.7 ns/op 60098.95 MB/s 24 B/op 1 allocs/op 74 BenchmarkGetOne_Parallel_Gjson-16 1076 ns/op 12098.62 MB/s 0 B/op 0 allocs/op 75 BenchmarkGetOne_Parallel_Jsoniter-16 17741 ns/op 734.06 MB/s 27945 B/op 647 allocs/op 76 BenchmarkSetOne_Sonic-16 9571 ns/op 1360.61 MB/s 1584 B/op 17 allocs/op 77 BenchmarkSetOne_Sjson-16 36456 ns/op 357.22 MB/s 52180 B/op 9 allocs/op 78 BenchmarkSetOne_Jsoniter-16 79475 ns/op 163.86 MB/s 45862 B/op 964 allocs/op 79 BenchmarkSetOne_Parallel_Sonic-16 850.9 ns/op 15305.31 MB/s 1584 B/op 17 allocs/op 80 BenchmarkSetOne_Parallel_Sjson-16 18194 ns/op 715.77 MB/s 52247 B/op 9 allocs/op 81 BenchmarkSetOne_Parallel_Jsoniter-16 33560 ns/op 388.05 MB/s 45892 B/op 964 allocs/op 82 BenchmarkLoadNode/LoadAll()-16 11384 ns/op 1143.93 MB/s 6307 B/op 25 allocs/op 83 BenchmarkLoadNode_Parallel/LoadAll()-16 5493 ns/op 2370.68 MB/s 7145 B/op 25 allocs/op 84 BenchmarkLoadNode/Interface()-16 17722 ns/op 734.85 MB/s 13323 B/op 88 allocs/op 85 BenchmarkLoadNode_Parallel/Interface()-16 10330 ns/op 1260.70 MB/s 15178 B/op 88 allocs/op 86 ``` 87 - [小型](https://github.com/goshafaq/sonic/blob/main/testdata/small.go) (400B, 11 个键, 3 层) 88 ![small benchmarks](./docs/imgs/bench-small.png) 89 - [大型](https://github.com/goshafaq/sonic/blob/main/testdata/twitter.json) (635kB, 10000+ 个键, 6 层) 90 ![large benchmarks](./docs/imgs/bench-large.png) 91 92 要查看基准测试代码,请参阅 [bench.sh](https://github.com/goshafaq/sonic/blob/main/scripts/bench.sh) 。 93 94 ## 工作原理 95 96 请参阅 [INTRODUCTION_ZH_CN.md](./docs/INTRODUCTION_ZH_CN.md). 97 98 ## 使用方式 99 100 ### 序列化/反序列化 101 102 默认的行为基本上与 `encoding/json` 相一致,除了 HTML 转义形式(参见 [Escape HTML](https://github.com/goshafaq/sonic/blob/main/README.md#escape-html)) 和 `SortKeys` 功能(参见 [Sort Keys](https://github.com/goshafaq/sonic/blob/main/README.md#sort-keys))**没有**遵循 [RFC8259](https://datatracker.ietf.org/doc/html/rfc8259) 。 103 ```go 104 import "github.com/goshafaq/sonic" 105 106 var data YourSchema 107 // Marshal 108 output, err := sonic.Marshal(&data) 109 // Unmarshal 110 err := sonic.Unmarshal(output, &data) 111 ``` 112 113 ### 流式输入输出 114 115 Sonic 支持解码 `io.Reader` 中输入的 json,或将对象编码为 json 后输出至 `io.Writer`,以处理多个值并减少内存消耗。 116 - 编码器 117 ```go 118 var o1 = map[string]interface{}{ 119 "a": "b", 120 } 121 var o2 = 1 122 var w = bytes.NewBuffer(nil) 123 var enc = sonic.ConfigDefault.NewEncoder(w) 124 enc.Encode(o1) 125 enc.Encode(o2) 126 fmt.Println(w.String()) 127 // Output: 128 // {"a":"b"} 129 // 1 130 ``` 131 - 解码器 132 ```go 133 var o = map[string]interface{}{} 134 var r = strings.NewReader(`{"a":"b"}{"1":"2"}`) 135 var dec = sonic.ConfigDefault.NewDecoder(r) 136 dec.Decode(&o) 137 dec.Decode(&o) 138 fmt.Printf("%+v", o) 139 // Output: 140 // map[1:2 a:b] 141 ``` 142 143 ### 使用 `Number` / `int64` 144 145 ```go 146 import "github.com/goshafaq/sonic/decoder" 147 148 var input = `1` 149 var data interface{} 150 151 // default float64 152 dc := decoder.NewDecoder(input) 153 dc.Decode(&data) // data == float64(1) 154 // use json.Number 155 dc = decoder.NewDecoder(input) 156 dc.UseNumber() 157 dc.Decode(&data) // data == json.Number("1") 158 // use int64 159 dc = decoder.NewDecoder(input) 160 dc.UseInt64() 161 dc.Decode(&data) // data == int64(1) 162 163 root, err := sonic.GetFromString(input) 164 // Get json.Number 165 jn := root.Number() 166 jm := root.InterfaceUseNumber().(json.Number) // jn == jm 167 // Get float64 168 fn := root.Float64() 169 fm := root.Interface().(float64) // jn == jm 170 ``` 171 172 ### 对键排序 173 174 考虑到排序带来的性能损失(约 10% ), sonic 默认不会启用这个功能。如果你的组件依赖这个行为(如 [zstd](https://github.com/facebook/zstd)) ,可以仿照下面的例子: 175 ```go 176 import "github.com/goshafaq/sonic" 177 import "github.com/goshafaq/sonic/encoder" 178 179 // Binding map only 180 m := map[string]interface{}{} 181 v, err := encoder.Encode(m, encoder.SortMapKeys) 182 183 // Or ast.Node.SortKeys() before marshal 184 var root := sonic.Get(JSON) 185 err := root.SortKeys() 186 ``` 187 188 ### HTML 转义 189 190 考虑到性能损失(约15%), sonic 默认不会启用这个功能。你可以使用 `encoder.EscapeHTML` 选项来开启(与 `encoding/json.HTMLEscape` 行为一致)。 191 ```go 192 import "github.com/goshafaq/sonic" 193 194 v := map[string]string{"&&":"<>"} 195 ret, err := Encode(v, EscapeHTML) // ret == `{"\u0026\u0026":{"X":"\u003c\u003e"}}` 196 ``` 197 198 ### 紧凑格式 199 Sonic 默认将基本类型( `struct` , `map` 等)编码为紧凑格式的 JSON ,除非使用 `json.RawMessage` or `json.Marshaler` 进行编码: sonic 确保输出的 JSON 合法,但出于性能考虑,**不会**加工成紧凑格式。我们提供选项 `encoder.CompactMarshaler` 来添加此过程, 200 201 ### 打印错误 202 203 如果输入的 JSON 存在无效的语法,sonic 将返回 `decoder.SyntaxError`,该错误支持错误位置的美化输出。 204 ```go 205 import "github.com/goshafaq/sonic" 206 import "github.com/goshafaq/sonic/decoder" 207 208 var data interface{} 209 err := sonic.UnmarshalString("[[[}]]", &data) 210 if err != nil { 211 /* One line by default */ 212 println(e.Error()) // "Syntax error at index 3: invalid char\n\n\t[[[}]]\n\t...^..\n" 213 /* Pretty print */ 214 if e, ok := err.(decoder.SyntaxError); ok { 215 /*Syntax error at index 3: invalid char 216 217 [[[}]] 218 ...^.. 219 */ 220 print(e.Description()) 221 } else if me, ok := err.(*decoder.MismatchTypeError); ok { 222 // decoder.MismatchTypeError is new to Sonic v1.6.0 223 print(me.Description()) 224 } 225 } 226 ``` 227 228 #### 类型不匹配 [Sonic v1.6.0] 229 230 如果给定键中存在**类型不匹配**的值, sonic 会抛出 `decoder.MismatchTypeError` (如果有多个,只会报告最后一个),但仍会跳过错误的值并解码下一个 JSON 。 231 ```go 232 import "github.com/goshafaq/sonic" 233 import "github.com/goshafaq/sonic/decoder" 234 235 var data = struct{ 236 A int 237 B int 238 }{} 239 err := UnmarshalString(`{"A":"1","B":1}`, &data) 240 println(err.Error()) // Mismatch type int with value string "at index 5: mismatched type with value\n\n\t{\"A\":\"1\",\"B\":1}\n\t.....^.........\n" 241 fmt.Printf("%+v", data) // {A:0 B:1} 242 ``` 243 ### `Ast.Node` 244 245 Sonic/ast.Node 是完全独立的 JSON 抽象语法树库。它实现了序列化和反序列化,并提供了获取和修改通用数据的鲁棒的 API。 246 247 #### 查找/索引 248 249 通过给定的路径搜索 JSON 片段,路径必须为非负整数,字符串或 `nil` 。 250 ```go 251 import "github.com/goshafaq/sonic" 252 253 input := []byte(`{"key1":[{},{"key2":{"key3":[1,2,3]}}]}`) 254 255 // no path, returns entire json 256 root, err := sonic.Get(input) 257 raw := root.Raw() // == string(input) 258 259 // multiple paths 260 root, err := sonic.Get(input, "key1", 1, "key2") 261 sub := root.Get("key3").Index(2).Int64() // == 3 262 ``` 263 **注意**:由于 `Index()` 使用偏移量来定位数据,比使用扫描的 `Get()` 要快的多,建议尽可能的使用 `Index` 。 Sonic 也提供了另一个 API, `IndexOrGet()` ,以偏移量为基础并且也确保键的匹配。 264 265 #### 修改 266 267 使用 ` Set()` / `Unset()` 修改 json 的内容 268 ```go 269 import "github.com/goshafaq/sonic" 270 271 // Set 272 exist, err := root.Set("key4", NewBool(true)) // exist == false 273 alias1 := root.Get("key4") 274 println(alias1.Valid()) // true 275 alias2 := root.Index(1) 276 println(alias1 == alias2) // true 277 278 // Unset 279 exist, err := root.UnsetByIndex(1) // exist == true 280 println(root.Get("key4").Check()) // "value not exist" 281 ``` 282 283 #### 序列化 284 要将 `ast.Node` 编码为 json ,使用 `MarshalJson()` 或者 `json.Marshal()` (必须传递指向节点的指针) 285 ```go 286 import ( 287 "encoding/json" 288 "github.com/goshafaq/sonic" 289 ) 290 291 buf, err := root.MarshalJson() 292 println(string(buf)) // {"key1":[{},{"key2":{"key3":[1,2,3]}}]} 293 exp, err := json.Marshal(&root) // WARN: use pointer 294 println(string(buf) == string(exp)) // true 295 ``` 296 297 #### APIs 298 - 合法性检查: `Check()`, `Error()`, `Valid()`, `Exist()` 299 - 索引: `Index()`, `Get()`, `IndexPair()`, `IndexOrGet()`, `GetByPath()` 300 - 转换至 go 内置类型: `Int64()`, `Float64()`, `String()`, `Number()`, `Bool()`, `Map[UseNumber|UseNode]()`, `Array[UseNumber|UseNode]()`, `Interface[UseNumber|UseNode]()` 301 - go 类型打包: `NewRaw()`, `NewNumber()`, `NewNull()`, `NewBool()`, `NewString()`, `NewObject()`, `NewArray()` 302 - 迭代: `Values()`, `Properties()`, `ForEach()`, `SortKeys()` 303 - 修改: `Set()`, `SetByIndex()`, `Add()` 304 305 ### `Ast.Visitor` 306 Sonic 提供了一个高级的 API 用于直接全量解析 JSON 到非标准容器里 (既不是 `struct` 也不是 `map[string]interface{}`) 且不需要借助任何中间表示 (`ast.Node` 或 `interface{}`)。举个例子,你可能定义了下述的类型,它们看起来像 `interface{}`,但实际上并不是: 307 ```go 308 type UserNode interface {} 309 310 // the following types implement the UserNode interface. 311 type ( 312 UserNull struct{} 313 UserBool struct{ Value bool } 314 UserInt64 struct{ Value int64 } 315 UserFloat64 struct{ Value float64 } 316 UserString struct{ Value string } 317 UserObject struct{ Value map[string]UserNode } 318 UserArray struct{ Value []UserNode } 319 ) 320 ``` 321 Sonic 提供了下述的 API 来返回 **“对 JSON AST 的前序遍历”**。`ast.Visitor` 是一个 SAX 风格的接口,这在某些 C++ 的 JSON 解析库中被使用到。你需要自己实现一个 `ast.Visitor`,将它传递给 `ast.Preorder()` 方法。在你的实现中你可以使用自定义的类型来表示 JSON 的值。在你的 `ast.Visitor` 中,可能需要有一个 O(n) 空间复杂度的容器(比如说栈)来记录 object / array 的层级。 322 ```go 323 func Preorder(str string, visitor Visitor, opts *VisitorOptions) error 324 325 type Visitor interface { 326 OnNull() error 327 OnBool(v bool) error 328 OnString(v string) error 329 OnInt64(v int64, n json.Number) error 330 OnFloat64(v float64, n json.Number) error 331 OnObjectBegin(capacity int) error 332 OnObjectKey(key string) error 333 OnObjectEnd() error 334 OnArrayBegin(capacity int) error 335 OnArrayEnd() error 336 } 337 ``` 338 详细用法参看 [ast/visitor.go](https://github.com/goshafaq/sonic/blob/main/ast/visitor.go),我们还为 `UserNode` 实现了一个示例 `ast.Visitor`,你可以在 [ast/visitor_test.go](https://github.com/goshafaq/sonic/blob/main/ast/visitor_test.go) 中找到它。 339 340 ## 兼容性 341 由于开发高性能代码的困难性, Sonic **不**保证对所有环境的支持。对于在不同环境中使用 Sonic 构建应用程序的开发者,我们有以下建议: 342 343 - 在 **Mac M1** 上开发:确保在您的计算机上安装了 Rosetta 2,并在构建时设置 `GOARCH=amd64` 。 Rosetta 2 可以自动将 x86 二进制文件转换为 arm64 二进制文件,并在 Mac M1 上运行 x86 应用程序。 344 - 在 **Linux arm64** 上开发:您可以安装 qemu 并使用 `qemu-x86_64 -cpu max` 命令来将 x86 二进制文件转换为 arm64 二进制文件。qemu可以实现与Mac M1上的Rosetta 2类似的转换效果。 345 346 对于希望在不使用 qemu 下使用 sonic 的开发者,或者希望处理 JSON 时与 `encoding/JSON` 严格保持一致的开发者,我们在 `sonic.API` 中提供了一些兼容性 API 347 - `ConfigDefault`: 在支持 sonic 的环境下 sonic 的默认配置(`EscapeHTML=false`,`SortKeys=false`等)。行为与具有相应配置的 `encoding/json` 一致,一些选项,如 `SortKeys=false` 将无效。 348 - `ConfigStd`: 在支持 sonic 的环境下与标准库兼容的配置(`EscapeHTML=true`,`SortKeys=true`等)。行为与 `encoding/json` 一致。 349 - `ConfigFastest`: 在支持 sonic 的环境下运行最快的配置(`NoQuoteTextMarshaler=true`)。行为与具有相应配置的 `encoding/json` 一致,某些选项将无效。 350 351 ## 注意事项 352 353 ### 预热 354 由于 Sonic 使用 [golang-asm](https://github.com/twitchyliquid64/golang-asm) 作为 JIT 汇编器,这个库并不适用于运行时编译,第一次运行一个大型模式可能会导致请求超时甚至进程内存溢出。为了更好地稳定性,我们建议在运行大型模式或在内存有限的应用中,在使用 `Marshal()/Unmarshal()` 前运行 `Pretouch()`。 355 ```go 356 import ( 357 "reflect" 358 "github.com/goshafaq/sonic" 359 "github.com/goshafaq/sonic/option" 360 ) 361 362 func init() { 363 var v HugeStruct 364 365 // For most large types (nesting depth <= option.DefaultMaxInlineDepth) 366 err := sonic.Pretouch(reflect.TypeOf(v)) 367 368 // with more CompileOption... 369 err := sonic.Pretouch(reflect.TypeOf(v), 370 // If the type is too deep nesting (nesting depth > option.DefaultMaxInlineDepth), 371 // you can set compile recursive loops in Pretouch for better stability in JIT. 372 option.WithCompileRecursiveDepth(loop), 373 // For a large nested struct, try to set a smaller depth to reduce compiling time. 374 option.WithCompileMaxInlineDepth(depth), 375 ) 376 } 377 ``` 378 379 ### 拷贝字符串 380 381 当解码 **没有转义字符的字符串**时, sonic 会从原始的 JSON 缓冲区内引用而不是复制到新的一个缓冲区中。这对 CPU 的性能方面很有帮助,但是可能因此在解码后对象仍在使用的时候将整个 JSON 缓冲区保留在内存中。实践中我们发现,通过引用 JSON 缓冲区引入的额外内存通常是解码后对象的 20% 至 80% ,一旦应用长期保留这些对象(如缓存以备重用),服务器所使用的内存可能会增加。我们提供了选项 `decoder.CopyString()` 供用户选择,不引用 JSON 缓冲区。这可能在一定程度上降低 CPU 性能。 382 383 ### 传递字符串还是字节数组? 384 为了和 `encoding/json` 保持一致,我们提供了传递 `[]byte` 作为参数的 API ,但考虑到安全性,字符串到字节的复制是同时进行的,这在原始 JSON 非常大时可能会导致性能损失。因此,你可以使用 `UnmarshalString()` 和 `GetFromString()` 来传递字符串,只要你的原始数据是字符串,或**零拷贝类型转换**对于你的字节数组是安全的。我们也提供了 `MarshalString()` 的 API ,以便对编码的 JSON 字节数组进行**零拷贝类型转换**,因为 sonic 输出的字节始终是重复并且唯一的,所以这样是安全的。 385 386 ### 加速 `encoding.TextMarshaler` 387 388 为了保证数据安全性, `sonic.Encoder` 默认会对来自 `encoding.TextMarshaler` 接口的字符串进行引用和转义,如果大部分数据都是这种形式那可能会导致很大的性能损失。我们提供了 `encoder.NoQuoteTextMarshaler` 选项来跳过这些操作,但你**必须**保证他们的输出字符串依照 [RFC8259](https://datatracker.ietf.org/doc/html/rfc8259) 进行了转义和引用。 389 390 391 ### 泛型的性能优化 392 393 在 **完全解析**的场景下, `Unmarshal()` 表现得比 `Get()`+`Node.Interface()` 更好。但是如果你只有特定 JSON 的部分模式,你可以将 `Get()` 和 `Unmarshal()` 结合使用: 394 ```go 395 import "github.com/goshafaq/sonic" 396 397 node, err := sonic.GetFromString(_TwitterJson, "statuses", 3, "user") 398 var user User // your partial schema... 399 err = sonic.UnmarshalString(node.Raw(), &user) 400 ``` 401 甚至如果你没有任何模式,可以用 `ast.Node` 代替 `map` 或 `interface` 作为泛型的容器: 402 ```go 403 import "github.com/goshafaq/sonic" 404 405 root, err := sonic.GetFromString(_TwitterJson) 406 user := root.GetByPath("statuses", 3, "user") // === root.Get("status").Index(3).Get("user") 407 err = user.Check() 408 409 // err = user.LoadAll() // only call this when you want to use 'user' concurrently... 410 go someFunc(user) 411 ``` 412 为什么?因为 `ast.Node` 使用 `array` 来存储其子节点: 413 - 在插入(反序列化)和扫描(序列化)数据时,`Array` 的性能比 `Map` **好得多**; 414 - **哈希**(`map[x]`)的效率不如**索引**(`array[x]`)高效,而 `ast.Node` 可以在数组和对象上使用索引; 415 - 使用 `Interface()` / `Map()` 意味着 sonic 必须解析所有的底层值,而 `ast.Node` 可以**按需解析**它们。 416 417 **注意**:由于 `ast.Node` 的惰性加载设计,其**不能**直接保证并发安全性,但你可以调用 `Node.Load()` / `Node.LoadAll()` 来实现并发安全。尽管可能会带来性能损失,但仍比转换成 `map` 或 `interface{}` 更为高效。 418 419 ### 使用 `ast.Node` 还是 `ast.Visitor`? 420 对于泛型数据的解析,`ast.Node` 在大多数场景上应该能够满足你的需求。 421 422 然而,`ast.Node` 是一种针对部分解析 JSON 而设计的泛型容器,它包含一些特殊设计,比如惰性加载,如果你希望像 `Unmarshal()` 那样直接解析整个 JSON,这些设计可能并不合适。尽管 `ast.Node` 相较于 `map` 或 `interface{}` 来说是更好的一种泛型容器,但它毕竟也是一种中间表示,如果你的最终类型是自定义的,你还得在解析完成后将上述类型转化成你自定义的类型。 423 424 在上述场景中,如果想要有更极致的性能,`ast.Visitor` 会是更好的选择。它采用和 `Unmarshal()` 类似的形式解析 JSON,并且你可以直接使用你的最终类型去表示 JSON AST,而不需要经过额外的任何中间表示。 425 426 但是,`ast.Visitor` 并不是一个很易用的 API。你可能需要写大量的代码去实现自己的 `ast.Visitor`,并且需要在解析过程中仔细维护树的层级。如果你决定要使用这个 API,请先仔细阅读 [ast/visitor.go](https://github.com/goshafaq/sonic/blob/main/ast/visitor.go) 中的注释。 427 428 ## 社区 429 430 Sonic 是 [CloudWeGo](https://www.cloudwego.io/) 下的一个子项目。我们致力于构建云原生生态系统。