最近接触到了 github.com/json-iterator/go , 是由滴滴开源的第三方json编码库,它同时提供Go和Java两个版本。
文中大量内容来自 github 上的 wiki 文档,有兴趣的朋友可以直接点击 Home 跳转到官方文档查阅。
本文加了些自己的思考以及相关的详细学习例子,废话不多说了,冲!!!
1、基础介绍
json-iterator提供简洁的API,可以让你很方便地进行json序列化/反序列化;与encoding/json完全兼容,使用者可以快速、方便地迁移到json-iterator上来。此外,json-iterator还提供了很多其他方便的功能,如开放的序列化/反序列化配置、Extension、FieldEncoder/FieldDecoder、懒解析Any对象等等增强功能,应对不同使用场景下的json编码和解析,满足各种复杂的需求
1.1、简单的API
-
序列化
type Student struct{Name stringAge intHeight float32}b, err := jsoniter.Marshal(Student{"Allen", 18, 180.43})type Student struct{ Name string Age int Height float32 } b, err := jsoniter.Marshal(Student{"Allen", 18, 180.43})
type Student struct{ Name string Age int Height float32 } b, err := jsoniter.Marshal(Student{"Allen", 18, 180.43})
-
反序列化
type Student struct{Name stringAge intHeight float32}var std Studenterr := jsoniter.Unmarshal([]byte(`{"Name":"Allen","Age":18,"Height":180.43}`), &std)type Student struct{ Name string Age int Height float32 } var std Student err := jsoniter.Unmarshal([]byte(`{"Name":"Allen","Age":18,"Height":180.43}`), &std)
type Student struct{ Name string Age int Height float32 } var std Student err := jsoniter.Unmarshal([]byte(`{"Name":"Allen","Age":18,"Height":180.43}`), &std)
1.2、替代encoding/json
encoding/json可以很方便地迁移到json-iterator,并且迁移前后代码行为保持一致。不管你是使用基本的Marshal/Unmarshal接口,或是使用Encoder/Decoder,或是你已有的Marshaler/Unmarshaler实现,都能正常地工作。
这一点还是挺重要的,尤其是对于替换成该 json 库的项目。
// import "encoding/json"//// json.Marshal(data)import "github.com/json-iterator/go"jsoniter.Marshal(data)// import "encoding/json" // // json.Marshal(data) import "github.com/json-iterator/go" jsoniter.Marshal(data)// import "encoding/json" // // json.Marshal(data) import "github.com/json-iterator/go" jsoniter.Marshal(data)
只需要把import的package替换成”github.com/json-iterator/go”,包名从”json”,替换成”jsoniter”即可
1.3、序列化/反序列化配置
json-iterator提供了几种序列化/反序列化配置,供不同的场景下的使用
api := jsoniter.Config{SortMapKeys:true}.Froze()b, err := api.Marshal(map[string]string{"C":"c", "A":"a", "B":"b"})api := jsoniter.Config{SortMapKeys:true}.Froze() b, err := api.Marshal(map[string]string{"C":"c", "A":"a", "B":"b"})api := jsoniter.Config{SortMapKeys:true}.Froze() b, err := api.Marshal(map[string]string{"C":"c", "A":"a", "B":"b"})
上面的例子中,我们开启了SortMapKeys
配置选项,让map序列化输出时字段进行排序输出。更多的选项说明,请参考Config章节
1.4、控制编解码行为
json-iterator提供了Extension
机制,我们可以通过注册自己的Extension
,来更精确地控制我们的序列化/反序列化行为
type sampleExtension struct {jsoniter.DummyExtension}func (e *sampleExtension) UpdateStructDescriptor(structDescriptor *jsoniter.StructDescriptor) {if structDescriptor.Type.String() != "main.testStruct" {return}binding := structDescriptor.GetField("TestField")binding.FromNames = []string{"TestField", "Test_Field", "Test-Field"}}type testStruct struct {TestField string}var t testStructjsoniter.RegisterExtension(&sampleExtension{})err := jsoniter.Unmarshal([]byte(`{"TestField":"aaa"}`), &t)err = jsoniter.Unmarshal([]byte(`{"Test_Field":"bbb"}`), &t)err = jsoniter.Unmarshal([]byte(`{"Test-Field":"ccc"}`), &t)type sampleExtension struct { jsoniter.DummyExtension } func (e *sampleExtension) UpdateStructDescriptor(structDescriptor *jsoniter.StructDescriptor) { if structDescriptor.Type.String() != "main.testStruct" { return } binding := structDescriptor.GetField("TestField") binding.FromNames = []string{"TestField", "Test_Field", "Test-Field"} } type testStruct struct { TestField string } var t testStruct jsoniter.RegisterExtension(&sampleExtension{}) err := jsoniter.Unmarshal([]byte(`{"TestField":"aaa"}`), &t) err = jsoniter.Unmarshal([]byte(`{"Test_Field":"bbb"}`), &t) err = jsoniter.Unmarshal([]byte(`{"Test-Field":"ccc"}`), &t)type sampleExtension struct { jsoniter.DummyExtension } func (e *sampleExtension) UpdateStructDescriptor(structDescriptor *jsoniter.StructDescriptor) { if structDescriptor.Type.String() != "main.testStruct" { return } binding := structDescriptor.GetField("TestField") binding.FromNames = []string{"TestField", "Test_Field", "Test-Field"} } type testStruct struct { TestField string } var t testStruct jsoniter.RegisterExtension(&sampleExtension{}) err := jsoniter.Unmarshal([]byte(`{"TestField":"aaa"}`), &t) err = jsoniter.Unmarshal([]byte(`{"Test_Field":"bbb"}`), &t) err = jsoniter.Unmarshal([]byte(`{"Test-Field":"ccc"}`), &t)
上面的例子中我们注册了一个Extension
,它指定了testStruct
的TestField
字段名绑定到哪些字符串上,所有绑定的字符串在解析时都当成是该字段。更多的说明请参考Extension章节
1.5、快速操作json对象
json-iterator提供了Any
对象,可以让你schemaless地从复杂嵌套json串中提取出感兴趣的部分
jsoniter.Get([]byte(`{"Field":{"InnerField":{"Name":"Allen"}}}`), "Field", "InnerField", "Name").ToString()// output: Allenjsoniter.Get([]byte(`{"Field":{"InnerField":{"Name":"Allen"}}}`), "Field", "InnerField", "Name").ToString() // output: Allenjsoniter.Get([]byte(`{"Field":{"InnerField":{"Name":"Allen"}}}`), "Field", "InnerField", "Name").ToString() // output: Allen
这里Get
返回的是一个Any
对象,我们获取嵌套结构体最内层的Name
字段的Any
对象,并调用ToString
转换得到我们要的”Allen”字符串。更多的说明请参考Any章节
2、Config (重点)
下来内容全部来自官网,Config_cn 点击上面链接即可跳转。
json-iterator提供了一些常用的序列化/反序列化选型供配置,使用者可以根据自己需求打开/关闭特定的选项
2.1、配置选项
要配置序列化/反序列化选项,你需要创建一个Config
结构体,并通过设置其字段来设置不同的选项,最后你还需要调用其Froze
方法来生成这个Config
对应的API
对象,通过这个API
对象来调用你的配置选项对应的序列化/反序列化函数。
api := jsoniter.Config{SortMapKeys:true}.Froze()api.Marshal(data)api := jsoniter.Config{SortMapKeys:true}.Froze() api.Marshal(data)api := jsoniter.Config{SortMapKeys:true}.Froze() api.Marshal(data)
这里我们创建了一个开启了SortMapKeys
选项的Config
,并生成其对应的API
对象,来进行序列化
2.2、内置配置
json-iterator提供了三个内置的配置:
-
ConfigDefault
ConfigDefault
是默认配置,它开启了EscapeHTML
选项。默认配置的意思是说,当你不创建自己的Config
对象并生成API
对象,而是直接以类似jsoniter.xxx
的方式调用接口时,实际上你的序列化/反序列化使用的就是这个ConfigDefault
配置 -
ConfigCompatibleWithStandardLibrary
ConfigCompatibleWithStandardLibrary
开启了EscapeHTML
、SortMapKeys
和ValidateJsonRawMessage
选项,当你需要近似100%地保证你的序列化/反序列化行为与encoding/json
保持一致时,你可以使用这个配置 -
ConfigFastest
ConfigFastest
关闭了EscapeHTML
,开启了MarshalFloatWith6Digits
和ObjectFieldMustBeSimpleString
选项,这个配置可以让你的序列化/反序列化达到最高效率,但会有某些限制
2.3、选项说明
-
IndentionStep
指定格式化序列化输出时的空格缩进数量
type Student struct{Name stringAge intHeight float32}// 四空格缩进的格式化输出c := jsoniter.Config{IndentionStep:4}.Froze()if s, err := c.MarshalToString(Student{"Allen", 18, 180.43}); err == nil{fmt.Println(s)// Output:// {// "Name": "Allen",// "Age": 18,// "Height": 180.43// }}type Student struct{ Name string Age int Height float32 } // 四空格缩进的格式化输出 c := jsoniter.Config{IndentionStep:4}.Froze() if s, err := c.MarshalToString(Student{"Allen", 18, 180.43}); err == nil{ fmt.Println(s) // Output: // { // "Name": "Allen", // "Age": 18, // "Height": 180.43 // } }
type Student struct{ Name string Age int Height float32 } // 四空格缩进的格式化输出 c := jsoniter.Config{IndentionStep:4}.Froze() if s, err := c.MarshalToString(Student{"Allen", 18, 180.43}); err == nil{ fmt.Println(s) // Output: // { // "Name": "Allen", // "Age": 18, // "Height": 180.43 // } }
-
MarshalFloatWith6Digits
指定浮点数序列化输出时最多保留6位小数
c := jsoniter.Config{MarshalFloatWith6Digits:true}.Froze()if s, err := c.MarshalToString(3.14159265358979); err == nil{fmt.Println(s)// Output:// 3.141593}c := jsoniter.Config{MarshalFloatWith6Digits:true}.Froze() if s, err := c.MarshalToString(3.14159265358979); err == nil{ fmt.Println(s) // Output: // 3.141593 }
c := jsoniter.Config{MarshalFloatWith6Digits:true}.Froze() if s, err := c.MarshalToString(3.14159265358979); err == nil{ fmt.Println(s) // Output: // 3.141593 }
-
EscapeHTML
开启了这个选项后,如果你的
string
类型的变量中含有HTML中使用的特殊字符(如'<‘,’>’,’&’等),序列化时它们会被转义输出type Text struct{Html string}c := jsoniter.Config{EscapeHTML:true}.Froze()if s, err := c.MarshalToString(Text{`<script>xxx</script>`}); err == nil{fmt.Println(s)// Output:// {"Html":"\u003cscript\u003exxx\u003c/script\u003e"}}type Text struct{ Html string } c := jsoniter.Config{EscapeHTML:true}.Froze() if s, err := c.MarshalToString(Text{`<script>xxx</script>`}); err == nil{ fmt.Println(s) // Output: // {"Html":"\u003cscript\u003exxx\u003c/script\u003e"} }
type Text struct{ Html string } c := jsoniter.Config{EscapeHTML:true}.Froze() if s, err := c.MarshalToString(Text{`<script>xxx</script>`}); err == nil{ fmt.Println(s) // Output: // {"Html":"\u003cscript\u003exxx\u003c/script\u003e"} }
-
SortMapKeys
指定
map
类型序列化输出时按照其key排序rgb := map[string][3]int{"yellow":{255, 255, 0},"red":{0, 0, 255},"green":{0, 255, 0},"blue":{0, 0, 255},}c := jsoniter.Config{SortMapKeys:true}.Froze()if s, err := c.MarshalToString(rgb); err == nil{fmt.Println(s)// 按key的字典序排序输出// Output:// {"blue":[0,0,255],"green":[0,255,0],"red":[0,0,255],"yellow":[255,255,0]}}rgb := map[string][3]int{ "yellow":{255, 255, 0}, "red":{0, 0, 255}, "green":{0, 255, 0}, "blue":{0, 0, 255}, } c := jsoniter.Config{SortMapKeys:true}.Froze() if s, err := c.MarshalToString(rgb); err == nil{ fmt.Println(s) // 按key的字典序排序输出 // Output: // {"blue":[0,0,255],"green":[0,255,0],"red":[0,0,255],"yellow":[255,255,0]} }
rgb := map[string][3]int{ "yellow":{255, 255, 0}, "red":{0, 0, 255}, "green":{0, 255, 0}, "blue":{0, 0, 255}, } c := jsoniter.Config{SortMapKeys:true}.Froze() if s, err := c.MarshalToString(rgb); err == nil{ fmt.Println(s) // 按key的字典序排序输出 // Output: // {"blue":[0,0,255],"green":[0,255,0],"red":[0,0,255],"yellow":[255,255,0]} }
-
UseNumber
指定反序列化时将数字(整数、浮点数)解析成
json.Number
类型。var number interface{}c := jsoniter.Config{UseNumber:true}.Froze()if err := c.UnmarshalFromString(`3.14159265358979`, &number); err == nil{if n, ok := number.(json.Number); ok{// 数字被解析成json.Number类型f, _ := n.Float64()fmt.Println(f)// Output:// 3.14159265358979}}var number interface{} c := jsoniter.Config{UseNumber:true}.Froze() if err := c.UnmarshalFromString(`3.14159265358979`, &number); err == nil{ if n, ok := number.(json.Number); ok{ // 数字被解析成json.Number类型 f, _ := n.Float64() fmt.Println(f) // Output: // 3.14159265358979 } }
var number interface{} c := jsoniter.Config{UseNumber:true}.Froze() if err := c.UnmarshalFromString(`3.14159265358979`, &number); err == nil{ if n, ok := number.(json.Number); ok{ // 数字被解析成json.Number类型 f, _ := n.Float64() fmt.Println(f) // Output: // 3.14159265358979 } }
-
DisallowUnknownFields
当开启该选项时,反序列化过程如果解析到未知字段,即在结构体的schema定义中找不到的字段时,不会跳过然后继续解析,而会返回错误
type Student struct{Name stringAge intHeight float32}var std Studentc := jsoniter.Config{DisallowUnknownFields:true}.Froze()// json串中包含未知字段"Weight"if err := c.UnmarshalFromString(`{"Name":"Allen","Age":18,"Height":180.43,"Weight":60.56}`, &std); err == nil{fmt.Println(std)}else{fmt.Println(err)// Output// main.Student.ReadObject: found unknown field: Weight, error found in #10 byte of ...|3,"Weight":60.56}|..., bigger context ...|{"Name":"Allen","Age":18,"Height":180.43,"Weight":60.56}|...}type Student struct{ Name string Age int Height float32 } var std Student c := jsoniter.Config{DisallowUnknownFields:true}.Froze() // json串中包含未知字段"Weight" if err := c.UnmarshalFromString(`{"Name":"Allen","Age":18,"Height":180.43,"Weight":60.56}`, &std); err == nil{ fmt.Println(std) }else{ fmt.Println(err) // Output // main.Student.ReadObject: found unknown field: Weight, error found in #10 byte of ...|3,"Weight":60.56}|..., bigger context ...|{"Name":"Allen","Age":18,"Height":180.43,"Weight":60.56}|... }
type Student struct{ Name string Age int Height float32 } var std Student c := jsoniter.Config{DisallowUnknownFields:true}.Froze() // json串中包含未知字段"Weight" if err := c.UnmarshalFromString(`{"Name":"Allen","Age":18,"Height":180.43,"Weight":60.56}`, &std); err == nil{ fmt.Println(std) }else{ fmt.Println(err) // Output // main.Student.ReadObject: found unknown field: Weight, error found in #10 byte of ...|3,"Weight":60.56}|..., bigger context ...|{"Name":"Allen","Age":18,"Height":180.43,"Weight":60.56}|... }
-
TagKey
指定tag字符串,默认情况为”json”,我们可以指定成另一个字符串
type Student struct{Name string `jsoniter:"name"`Age intHeight float32 `jsoniter:"-"`}// 将tag指定为"jsoniter"c := jsoniter.Config{TagKey:"jsoniter"}.Froze()if s, err := c.MarshalToString(Student{"Allen", 18, 180.43}); err == nil{fmt.Println(s)// Output:// {"name":"Allen","Age":18}}type Student struct{ Name string `jsoniter:"name"` Age int Height float32 `jsoniter:"-"` } // 将tag指定为"jsoniter" c := jsoniter.Config{TagKey:"jsoniter"}.Froze() if s, err := c.MarshalToString(Student{"Allen", 18, 180.43}); err == nil{ fmt.Println(s) // Output: // {"name":"Allen","Age":18} }
type Student struct{ Name string `jsoniter:"name"` Age int Height float32 `jsoniter:"-"` } // 将tag指定为"jsoniter" c := jsoniter.Config{TagKey:"jsoniter"}.Froze() if s, err := c.MarshalToString(Student{"Allen", 18, 180.43}); err == nil{ fmt.Println(s) // Output: // {"name":"Allen","Age":18} }
-
OnlyTaggedField
当开启该选项时,只有带上tag的结构体字段才会被序列化输出
type Student struct{Name string `json:"name"`Age intHeight float32 `json:",omitempty"`}c := jsoniter.Config{OnlyTaggedField:true}.Froze()if s, err := c.MarshalToString(Student{"Allen", 18, 180.43}); err == nil{fmt.Println(s)// Age字段没有tag,不会编码输出// Output:// {"name":"Allen","Height":180.43}}type Student struct{ Name string `json:"name"` Age int Height float32 `json:",omitempty"` } c := jsoniter.Config{OnlyTaggedField:true}.Froze() if s, err := c.MarshalToString(Student{"Allen", 18, 180.43}); err == nil{ fmt.Println(s) // Age字段没有tag,不会编码输出 // Output: // {"name":"Allen","Height":180.43} }
type Student struct{ Name string `json:"name"` Age int Height float32 `json:",omitempty"` } c := jsoniter.Config{OnlyTaggedField:true}.Froze() if s, err := c.MarshalToString(Student{"Allen", 18, 180.43}); err == nil{ fmt.Println(s) // Age字段没有tag,不会编码输出 // Output: // {"name":"Allen","Height":180.43} }
-
ValidateJsonRawMessage
json.RawMessage
类型的字段在序列化时会原封不动地进行输出。开启这个选项后,json-iterator会校验这种类型的字段包含的是否一个合法的json串,如果合法,原样输出;否则会输出”null”type Book struct{Pages intName stringDescription json.RawMessage}c := jsoniter.Config{ValidateJsonRawMessage:true}.Froze()if s, err := c.MarshalToString(Book{361, "X",json.RawMessage(`{"Category":`)}); err == nil{fmt.Println(s)// Description 字段为非法json串,输出null// Output:// {"Pages":361,"Name":"X","Description":null}}type Book struct{ Pages int Name string Description json.RawMessage } c := jsoniter.Config{ValidateJsonRawMessage:true}.Froze() if s, err := c.MarshalToString(Book{361, "X",json.RawMessage(`{"Category":`)}); err == nil{ fmt.Println(s) // Description 字段为非法json串,输出null // Output: // {"Pages":361,"Name":"X","Description":null} }
type Book struct{ Pages int Name string Description json.RawMessage } c := jsoniter.Config{ValidateJsonRawMessage:true}.Froze() if s, err := c.MarshalToString(Book{361, "X",json.RawMessage(`{"Category":`)}); err == nil{ fmt.Println(s) // Description 字段为非法json串,输出null // Output: // {"Pages":361,"Name":"X","Description":null} }
-
ObjectFieldMustBeSimpleString
开启该选项后,反序列化过程中不会对你的json串中对象的字段字符串可能包含的转义进行处理,因此你应该保证你的待解析json串中对象的字段应该是简单的字符串(不包含转义)
type Student struct{Name stringAge intHeight float32}var std Studentc := jsoniter.Config{ObjectFieldMustBeSimpleString:true}.Froze()if err := c.UnmarshalFromString(`{"Name":"Allen","Ag\u0065":18,"Height":180.43}`, &std); err == nil{fmt.Println(std)// Age字段的转义不会处理,因此该字段无法解析出来// Output:// {Allen 0 180.43}}type Student struct{ Name string Age int Height float32 } var std Student c := jsoniter.Config{ObjectFieldMustBeSimpleString:true}.Froze() if err := c.UnmarshalFromString(`{"Name":"Allen","Ag\u0065":18,"Height":180.43}`, &std); err == nil{ fmt.Println(std) // Age字段的转义不会处理,因此该字段无法解析出来 // Output: // {Allen 0 180.43} }
type Student struct{ Name string Age int Height float32 } var std Student c := jsoniter.Config{ObjectFieldMustBeSimpleString:true}.Froze() if err := c.UnmarshalFromString(`{"Name":"Allen","Ag\u0065":18,"Height":180.43}`, &std); err == nil{ fmt.Println(std) // Age字段的转义不会处理,因此该字段无法解析出来 // Output: // {Allen 0 180.43} }
-
CaseSensitive
开启该选项后,你的待解析json串中的对象的字段必须与你的schema定义的字段大小写严格一致
type Student struct{Name stringAge intHeight float32}var std Studentc := jsoniter.Config{CaseSensitive:true}.Froze()if err := c.UnmarshalFromString(`{"Name":"Allen","Age":18,"height":180.43}`, &std); err == nil{fmt.Println(std)// Height字段的大小写不一致,无法解析出来// Output:// {Allen 18 0}}type Student struct{ Name string Age int Height float32 } var std Student c := jsoniter.Config{CaseSensitive:true}.Froze() if err := c.UnmarshalFromString(`{"Name":"Allen","Age":18,"height":180.43}`, &std); err == nil{ fmt.Println(std) // Height字段的大小写不一致,无法解析出来 // Output: // {Allen 18 0} }
type Student struct{ Name string Age int Height float32 } var std Student c := jsoniter.Config{CaseSensitive:true}.Froze() if err := c.UnmarshalFromString(`{"Name":"Allen","Age":18,"height":180.43}`, &std); err == nil{ fmt.Println(std) // Height字段的大小写不一致,无法解析出来 // Output: // {Allen 18 0} }
我们知道,当我们为为某个类型实现了
MarshalJSON()([]byte, error)和
UnmarshalJSON(b []byte) error方法,那么这个类型在序列化(MarshalJSON)/反序列化(UnmarshalJSON)时就会使用你定制的相应方法。
知道了这个前提知识后,我们来一起看看如何自定义 序列化和反序列化方式。
3、Extension
Config
可以提供部分选项来控制序列化/反序列化的行为,但是不能提供更精细的编码或解析控制,无法应对复杂的需求。json-iterator考虑了这一点,提供了Extension
的机制,来满足复杂的序列化/反序列化场景。
3.1、ValEncoder/ValDecoder接口
在介绍Extension
的使用之前,需要先介绍一下ValEncoder
和ValDecoder
,因为Extension
的本质上就是针对不同的类型创建不同的ValEncoder
和ValDecoder
实现的。注意,ValEncoder
/ValDecoder
和json.Encoder
/json.Decoder
是不一样的概念,不要混淆了。
-
ValEncoder
type ValEncoder interface {IsEmpty(ptr unsafe.Pointer) boolEncode(ptr unsafe.Pointer, stream *Stream)}type ValEncoder interface { IsEmpty(ptr unsafe.Pointer) bool Encode(ptr unsafe.Pointer, stream *Stream) }
type ValEncoder interface { IsEmpty(ptr unsafe.Pointer) bool Encode(ptr unsafe.Pointer, stream *Stream) }
ValEncoder
实际上是json-iterator内部用于针对某个类型的数据进行序列化编码的编码器,它的两个成员函数说明如下:-
Encode
Encode
函数用于实现某个类型数据的编码,ptr
是指向当前待编码数据的指针,stream
提供不同的接口供使用者将各种类型的数据写入到输出设备(详见Stream章节)。那么,在这个函数里面,我们怎么实现编码呢?实际上,我们大部分时间做的,就是将ptr
转换成这个ValEncoder
对应的数据类型的指针,然后调用stream
的接口,将ptr
指向的数值进行编码输出 -
IsEmpty
IsEmpty
是跟omitempty
这个tag相关的函数。我们都知道,在一个结构体里面,如果某个字段的tag带上了omitempty
属性,那么当这个字段对应的”数值为空”时,这个字段在序列化时不会被编码输出。那么什么叫”数值为空”呢?对于不同类型的数据,恐怕应该是有不同的定义的。因此IsEmpty
这个函数里面,就是需要你去实现,你的ValEncoder
对应的数据类型在实际数值是什么的时候,称作”数值为空”
我们看一个具体的例子,来帮助我们理解
ValEncoder
。json-iterator提供了一个内置的TimeAsInt64Codec
,来看看它的实现:func (codec *timeAsInt64Codec) IsEmpty(ptr unsafe.Pointer) bool {ts := *((*time.Time)(ptr))return ts.UnixNano() == 0}/*在这种情况下,`ptr` 被声明为 `unsafe.Pointer`,并且进行了类型转换操作 `*((*time.Time)(ptr))`。让我们逐步解释这个表达式:1. `unsafe.Pointer`:`unsafe.Pointer` 是 Go 语言中的一个特殊类型,它可以表示任何指针类型,但是使用它可能会绕过类型安全检查,因此需要非常小心使用。2. `(ptr)`:这里的 `(ptr)` 将 `unsafe.Pointer` 类型转换为 `*time.Time` 指针类型。这是一个类型断言(type assertion),将一个指针从 `unsafe.Pointer` 转换为 `*time.Time` 类型的指针。3. `*((*time.Time)(ptr))`:外层的 `*` 表示这是一个指针类型的表达式,即一个指向 `time.Time` 类型的指针。这实际上是将 `unsafe.Pointer` 转换后的指针再次转换回 `*time.Time` 指针类型。综上所述,`*((*time.Time)(ptr))` 表达的含义是将 `unsafe.Pointer` 类型的指针 `ptr` 首先转换为 `*time.Time` 指针类型,然后通过解引用该指针,可以获取到指向 `time.Time` 类型对象的引用。这种使用 `unsafe.Pointer` 进行类型转换的操作需要格外小心,因为它绕过了 Go 语言的类型安全性,可能会导致未定义行为或内存问题。* 有两种作用,一个是定义指针类型,一个是根据内容地址取对应的值,这里最外面的 * 就是根据内存地址取值。*/func (codec *timeAsInt64Codec) Encode(ptr unsafe.Pointer, stream *jsoniter.Stream) {ts := *((*time.Time)(ptr))stream.WriteInt64(ts.UnixNano() / codec.precision.Nanoseconds())}func (codec *timeAsInt64Codec) IsEmpty(ptr unsafe.Pointer) bool { ts := *((*time.Time)(ptr)) return ts.UnixNano() == 0 } /* 在这种情况下,`ptr` 被声明为 `unsafe.Pointer`,并且进行了类型转换操作 `*((*time.Time)(ptr))`。让我们逐步解释这个表达式: 1. `unsafe.Pointer`:`unsafe.Pointer` 是 Go 语言中的一个特殊类型,它可以表示任何指针类型,但是使用它可能会绕过类型安全检查,因此需要非常小心使用。 2. `(ptr)`:这里的 `(ptr)` 将 `unsafe.Pointer` 类型转换为 `*time.Time` 指针类型。这是一个类型断言(type assertion),将一个指针从 `unsafe.Pointer` 转换为 `*time.Time` 类型的指针。 3. `*((*time.Time)(ptr))`:外层的 `*` 表示这是一个指针类型的表达式,即一个指向 `time.Time` 类型的指针。这实际上是将 `unsafe.Pointer` 转换后的指针再次转换回 `*time.Time` 指针类型。 综上所述,`*((*time.Time)(ptr))` 表达的含义是将 `unsafe.Pointer` 类型的指针 `ptr` 首先转换为 `*time.Time` 指针类型,然后通过解引用该指针,可以获取到指向 `time.Time` 类型对象的引用。这种使用 `unsafe.Pointer` 进行类型转换的操作需要格外小心,因为它绕过了 Go 语言的类型安全性,可能会导致未定义行为或内存问题。 * 有两种作用,一个是定义指针类型,一个是根据内容地址取对应的值,这里最外面的 * 就是根据内存地址取值。 */ func (codec *timeAsInt64Codec) Encode(ptr unsafe.Pointer, stream *jsoniter.Stream) { ts := *((*time.Time)(ptr)) stream.WriteInt64(ts.UnixNano() / codec.precision.Nanoseconds()) }
func (codec *timeAsInt64Codec) IsEmpty(ptr unsafe.Pointer) bool { ts := *((*time.Time)(ptr)) return ts.UnixNano() == 0 } /* 在这种情况下,`ptr` 被声明为 `unsafe.Pointer`,并且进行了类型转换操作 `*((*time.Time)(ptr))`。让我们逐步解释这个表达式: 1. `unsafe.Pointer`:`unsafe.Pointer` 是 Go 语言中的一个特殊类型,它可以表示任何指针类型,但是使用它可能会绕过类型安全检查,因此需要非常小心使用。 2. `(ptr)`:这里的 `(ptr)` 将 `unsafe.Pointer` 类型转换为 `*time.Time` 指针类型。这是一个类型断言(type assertion),将一个指针从 `unsafe.Pointer` 转换为 `*time.Time` 类型的指针。 3. `*((*time.Time)(ptr))`:外层的 `*` 表示这是一个指针类型的表达式,即一个指向 `time.Time` 类型的指针。这实际上是将 `unsafe.Pointer` 转换后的指针再次转换回 `*time.Time` 指针类型。 综上所述,`*((*time.Time)(ptr))` 表达的含义是将 `unsafe.Pointer` 类型的指针 `ptr` 首先转换为 `*time.Time` 指针类型,然后通过解引用该指针,可以获取到指向 `time.Time` 类型对象的引用。这种使用 `unsafe.Pointer` 进行类型转换的操作需要格外小心,因为它绕过了 Go 语言的类型安全性,可能会导致未定义行为或内存问题。 * 有两种作用,一个是定义指针类型,一个是根据内容地址取对应的值,这里最外面的 * 就是根据内存地址取值。 */ func (codec *timeAsInt64Codec) Encode(ptr unsafe.Pointer, stream *jsoniter.Stream) { ts := *((*time.Time)(ptr)) stream.WriteInt64(ts.UnixNano() / codec.precision.Nanoseconds()) }
Encode
函数中,将ptr
转换成指向time.Time
类型的指针,然后对其解引用拿到了其指向的time.Time
对象。接下来调用其成员函数计算出它对应的unix时间,最后调用stream
的写入接口将这个int64
的unix时间数值进行编码输出,这样就完成了将原本以对象方式输出的time.Time
数值,转换成int64
类型的unix时间输出IsEmpty
通过同样方式拿到ptr
指向的time.Time
对象,然后将time.Time
类型”数值为空”定义为其转换出来的unix时间为0 -
-
ValDecoder
type ValDecoder interface {Decode(ptr unsafe.Pointer, iter *Iterator)}type ValDecoder interface { Decode(ptr unsafe.Pointer, iter *Iterator) }
type ValDecoder interface { Decode(ptr unsafe.Pointer, iter *Iterator) }
ValEncoder
实际上是json-iterator内部用于针对某个类型的数据进行反序列化解码的解码器,它的成员函数说明如下:-
Decode
Decode
函数用于实现某个类型数据的解码,ptr
是指向当前待写入数据的指针,iter
提供不同的接口供使用者将各种类型的数据从输入源读入(详见Iterator章节)。那么,在这个函数里面,我们怎么实现解码呢?首先,我们调用iter
提供的接口,从json串的输入源读入ValDecoder
对应类型的数据,然后将ptr
做一个强转,将其转换成指向ValDecoder
对应类型的指针,然后将该指针指向的数据设置成我们通过iter
接口读取出来的值
还是看
TimeAsInt64Codec
的例子func (codec *timeAsInt64Codec) Decode(ptr unsafe.Pointer, iter *jsoniter.Iterator) {nanoseconds := iter.ReadInt64() * codec.precision.Nanoseconds()*((*time.Time)(ptr)) = time.Unix(0, nanoseconds)}func (codec *timeAsInt64Codec) Decode(ptr unsafe.Pointer, iter *jsoniter.Iterator) { nanoseconds := iter.ReadInt64() * codec.precision.Nanoseconds() *((*time.Time)(ptr)) = time.Unix(0, nanoseconds) }
func (codec *timeAsInt64Codec) Decode(ptr unsafe.Pointer, iter *jsoniter.Iterator) { nanoseconds := iter.ReadInt64() * codec.precision.Nanoseconds() *((*time.Time)(ptr)) = time.Unix(0, nanoseconds) }
Decode
函数中,调用iter
的接口从json输入源中读取了一个int64
的数值,接下来因为我们这个ValDecoder
对应的数据类型是time.Time
,这里把ptr
转换成指向time.Time
类型的指针,并以我们读入的int64
数值为unix时间初始化了一个time.Time
对象,最后将它赋给ptr
指向的数值。这样,我们就完成了从json串中读入unix时间,并将其转换成time.Time
对象的功能 -
3.2、定制你的扩展
要定制序列化/反序列化扩展,需要实现Extension
接口,并通过RegisterExtension
进行注册,Extension
包含以下方法:
type Extension interface {UpdateStructDescriptor(structDescriptor *StructDescriptor)CreateMapKeyDecoder(typ reflect2.Type) ValDecoderCreateMapKeyEncoder(typ reflect2.Type) ValEncoderCreateDecoder(typ reflect2.Type) ValDecoderCreateEncoder(typ reflect2.Type) ValEncoderDecorateDecoder(typ reflect2.Type, decoder ValDecoder) ValDecoderDecorateEncoder(typ reflect2.Type, encoder ValEncoder) ValEncoder}type Extension interface { UpdateStructDescriptor(structDescriptor *StructDescriptor) CreateMapKeyDecoder(typ reflect2.Type) ValDecoder CreateMapKeyEncoder(typ reflect2.Type) ValEncoder CreateDecoder(typ reflect2.Type) ValDecoder CreateEncoder(typ reflect2.Type) ValEncoder DecorateDecoder(typ reflect2.Type, decoder ValDecoder) ValDecoder DecorateEncoder(typ reflect2.Type, encoder ValEncoder) ValEncoder }type Extension interface { UpdateStructDescriptor(structDescriptor *StructDescriptor) CreateMapKeyDecoder(typ reflect2.Type) ValDecoder CreateMapKeyEncoder(typ reflect2.Type) ValEncoder CreateDecoder(typ reflect2.Type) ValDecoder CreateEncoder(typ reflect2.Type) ValEncoder DecorateDecoder(typ reflect2.Type, decoder ValDecoder) ValDecoder DecorateEncoder(typ reflect2.Type, encoder ValEncoder) ValEncoder }
当然,很多情况下,我们只需要用到里面的部分功能。json-iterator里面提供了一个DummyExtension
,它是一个最基础的Extension
实现(基本什么都不做或返回空)。当你在定义自己的Extension
时,你可以匿名地嵌入DummyExtension
,这样你就不需要实现所有的Extension
成员,只需要关注自己需要的功能。下面我们通过一些例子,来说明Extension
的各个成员函数可以用来做什么
-
UpdateStructDescriptor
UpdateStructDescriptor
函数中,我们可以对结构体的某个字段定制其编码/解码器,或者控制该字段序列化/反序列化时与哪些字符串绑定type testCodec struct{}func (codec *testCodec) Encode(ptr unsafe.Pointer, stream *jsoniter.Stream){str := *((*string)(ptr))stream.WriteString("TestPrefix_" + str)}func (codec *testCodec) IsEmpty(ptr unsafe.Pointer) bool {str := *((*string)(ptr))return str == ""}type sampleExtension struct {jsoniter.DummyExtension}func (e *sampleExtension) UpdateStructDescriptor(structDescriptor *jsoniter.StructDescriptor) {// 这个判断保证我们只针对testStruct结构体,对其他类型无效if structDescriptor.Type.String() != "main.testStruct" {return}binding := structDescriptor.GetField("TestField")binding.Encoder = &testCodec{}binding.FromNames = []string{"TestField", "Test_Field", "Test-Field"}}func extensionTest(){type testStruct struct {TestField string}t := testStruct{"fieldValue"}jsoniter.RegisterExtension(&sampleExtension{})s, _ := jsoniter.MarshalToString(t)fmt.Println(s)// Output:// {"TestField":"TestPrefix_fieldValue"}jsoniter.UnmarshalFromString(`{"Test-Field":"bbb"}`, &t)fmt.Println(t.TestField)// Output:// bbb}type testCodec struct{ } func (codec *testCodec) Encode(ptr unsafe.Pointer, stream *jsoniter.Stream){ str := *((*string)(ptr)) stream.WriteString("TestPrefix_" + str) } func (codec *testCodec) IsEmpty(ptr unsafe.Pointer) bool { str := *((*string)(ptr)) return str == "" } type sampleExtension struct { jsoniter.DummyExtension } func (e *sampleExtension) UpdateStructDescriptor(structDescriptor *jsoniter.StructDescriptor) { // 这个判断保证我们只针对testStruct结构体,对其他类型无效 if structDescriptor.Type.String() != "main.testStruct" { return } binding := structDescriptor.GetField("TestField") binding.Encoder = &testCodec{} binding.FromNames = []string{"TestField", "Test_Field", "Test-Field"} } func extensionTest(){ type testStruct struct { TestField string } t := testStruct{"fieldValue"} jsoniter.RegisterExtension(&sampleExtension{}) s, _ := jsoniter.MarshalToString(t) fmt.Println(s) // Output: // {"TestField":"TestPrefix_fieldValue"} jsoniter.UnmarshalFromString(`{"Test-Field":"bbb"}`, &t) fmt.Println(t.TestField) // Output: // bbb }
type testCodec struct{ } func (codec *testCodec) Encode(ptr unsafe.Pointer, stream *jsoniter.Stream){ str := *((*string)(ptr)) stream.WriteString("TestPrefix_" + str) } func (codec *testCodec) IsEmpty(ptr unsafe.Pointer) bool { str := *((*string)(ptr)) return str == "" } type sampleExtension struct { jsoniter.DummyExtension } func (e *sampleExtension) UpdateStructDescriptor(structDescriptor *jsoniter.StructDescriptor) { // 这个判断保证我们只针对testStruct结构体,对其他类型无效 if structDescriptor.Type.String() != "main.testStruct" { return } binding := structDescriptor.GetField("TestField") binding.Encoder = &testCodec{} binding.FromNames = []string{"TestField", "Test_Field", "Test-Field"} } func extensionTest(){ type testStruct struct { TestField string } t := testStruct{"fieldValue"} jsoniter.RegisterExtension(&sampleExtension{}) s, _ := jsoniter.MarshalToString(t) fmt.Println(s) // Output: // {"TestField":"TestPrefix_fieldValue"} jsoniter.UnmarshalFromString(`{"Test-Field":"bbb"}`, &t) fmt.Println(t.TestField) // Output: // bbb }
上面的例子,首先我们用
testCodec
实现了一个ValEncoder
,它编码时在字符串的前面加了一个”TestPrefix_”的前缀再输出。接着我们注册了一个sampleExtension
,在UpdateStructDescriptor
函数中我们将testStruct
的TestField
字段的编码器设置为我们的testCodec
,最后将其与几个别名字符串进行了绑定。得到的效果就是,这个结构体序列化输出时,TestField
的内容会添加上”TestPrefix_”前缀;而反序列化时,TestField
的别名都将映射成这个字段 -
CreateDecoder
-
CreateEncoder
CreateDecoder
和CreateEncoder
分别用来创建某个数据类型对应的解码器/编码器type wrapCodec struct{encodeFunc func(ptr unsafe.Pointer, stream *jsoniter.Stream)isEmptyFunc func(ptr unsafe.Pointer) booldecodeFunc func(ptr unsafe.Pointer, iter *jsoniter.Iterator)}func (codec *wrapCodec) Encode(ptr unsafe.Pointer, stream *jsoniter.Stream) {codec.encodeFunc(ptr, stream)}func (codec *wrapCodec) IsEmpty(ptr unsafe.Pointer) bool {if codec.isEmptyFunc == nil {return false}return codec.isEmptyFunc(ptr)}func (codec *wrapCodec) Decode(ptr unsafe.Pointer, iter *jsoniter.Iterator) {codec.decodeFunc(ptr, iter)}type sampleExtension struct {jsoniter.DummyExtension}func (e *sampleExtension) CreateDecoder(typ reflect2.Type) jsoniter.ValDecoder {if typ.Kind() == reflect.Int {return &wrapCodec{decodeFunc:func(ptr unsafe.Pointer, iter *jsoniter.Iterator) {i := iter.ReadInt()*(*int)(ptr) = i - 1000},}}return nil}func (e *sampleExtension) CreateEncoder(typ reflect2.Type) jsoniter.ValEncoder {if typ.Kind() == reflect.Int {return &wrapCodec{encodeFunc:func(ptr unsafe.Pointer, stream *jsoniter.Stream) {stream.WriteInt(*(*int)(ptr) + 1000)},isEmptyFunc:nil,}}return nil}func extensionTest(){i := 20000jsoniter.RegisterExtension(&sampleExtension{})s, _ := jsoniter.MarshalToString(i)fmt.Println(s)// Output:// 21000jsoniter.UnmarshalFromString(`30000`, &i)fmt.Println(i)// Output:// 29000}type wrapCodec struct{ encodeFunc func(ptr unsafe.Pointer, stream *jsoniter.Stream) isEmptyFunc func(ptr unsafe.Pointer) bool decodeFunc func(ptr unsafe.Pointer, iter *jsoniter.Iterator) } func (codec *wrapCodec) Encode(ptr unsafe.Pointer, stream *jsoniter.Stream) { codec.encodeFunc(ptr, stream) } func (codec *wrapCodec) IsEmpty(ptr unsafe.Pointer) bool { if codec.isEmptyFunc == nil { return false } return codec.isEmptyFunc(ptr) } func (codec *wrapCodec) Decode(ptr unsafe.Pointer, iter *jsoniter.Iterator) { codec.decodeFunc(ptr, iter) } type sampleExtension struct { jsoniter.DummyExtension } func (e *sampleExtension) CreateDecoder(typ reflect2.Type) jsoniter.ValDecoder { if typ.Kind() == reflect.Int { return &wrapCodec{ decodeFunc:func(ptr unsafe.Pointer, iter *jsoniter.Iterator) { i := iter.ReadInt() *(*int)(ptr) = i - 1000 }, } } return nil } func (e *sampleExtension) CreateEncoder(typ reflect2.Type) jsoniter.ValEncoder { if typ.Kind() == reflect.Int { return &wrapCodec{ encodeFunc:func(ptr unsafe.Pointer, stream *jsoniter.Stream) { stream.WriteInt(*(*int)(ptr) + 1000) }, isEmptyFunc:nil, } } return nil } func extensionTest(){ i := 20000 jsoniter.RegisterExtension(&sampleExtension{}) s, _ := jsoniter.MarshalToString(i) fmt.Println(s) // Output: // 21000 jsoniter.UnmarshalFromString(`30000`, &i) fmt.Println(i) // Output: // 29000 }
type wrapCodec struct{ encodeFunc func(ptr unsafe.Pointer, stream *jsoniter.Stream) isEmptyFunc func(ptr unsafe.Pointer) bool decodeFunc func(ptr unsafe.Pointer, iter *jsoniter.Iterator) } func (codec *wrapCodec) Encode(ptr unsafe.Pointer, stream *jsoniter.Stream) { codec.encodeFunc(ptr, stream) } func (codec *wrapCodec) IsEmpty(ptr unsafe.Pointer) bool { if codec.isEmptyFunc == nil { return false } return codec.isEmptyFunc(ptr) } func (codec *wrapCodec) Decode(ptr unsafe.Pointer, iter *jsoniter.Iterator) { codec.decodeFunc(ptr, iter) } type sampleExtension struct { jsoniter.DummyExtension } func (e *sampleExtension) CreateDecoder(typ reflect2.Type) jsoniter.ValDecoder { if typ.Kind() == reflect.Int { return &wrapCodec{ decodeFunc:func(ptr unsafe.Pointer, iter *jsoniter.Iterator) { i := iter.ReadInt() *(*int)(ptr) = i - 1000 }, } } return nil } func (e *sampleExtension) CreateEncoder(typ reflect2.Type) jsoniter.ValEncoder { if typ.Kind() == reflect.Int { return &wrapCodec{ encodeFunc:func(ptr unsafe.Pointer, stream *jsoniter.Stream) { stream.WriteInt(*(*int)(ptr) + 1000) }, isEmptyFunc:nil, } } return nil } func extensionTest(){ i := 20000 jsoniter.RegisterExtension(&sampleExtension{}) s, _ := jsoniter.MarshalToString(i) fmt.Println(s) // Output: // 21000 jsoniter.UnmarshalFromString(`30000`, &i) fmt.Println(i) // Output: // 29000 }
上面的例子我们用
wrapCodec
实现了ValEncoder
和ValDecoder
,然后我们注册了一个Extension
,这个Extension
的CreateEncoder
函数中设置了wrapCodec
的Encode
函数,指定对于Int
类型的数值+1000后输出;CreateDecoder
函数中设置了wrapCodec
的Decode
函数,指定读取了Int
类型的数值后,-1000再进行赋值。这里要注意的是,不管是CreateEncoder
还是CreateDecoder
函数,我们都通过其typ
参数限定了这个编码/解码器只对Int
类型生效 -
CreateMapKeyDecoder
-
CreateMapKeyEncoder
CreateMapKeyDecoder
和CreateMapKeyEncoder
跟上面的CreateDecoder
和CreateEncoder
用法差不多,只不过他们的生效对象是map
类型的key
的,这里不再举例详述了。 -
DecorateDecoder
-
DecorateEncoder
DecorateDecoder
和DecorateEncoder
可以用于装饰现有的ValEncoder
和ValEncoder
。考虑这么一个例子,在上述的CreateDecoder
和CreateEncoder
的说明中所举例的基础上,我们想再做一层扩展。当我们遇到数字字符串时,我们希望也可以解析成整形数,并且要复用基础例子中的解码器,这时候我们就需要用到装饰器。type decorateExtension struct{jsoniter.DummyExtension}type decorateCodec struct{originDecoder jsoniter.ValDecoder}func (codec *decorateCodec) Decode(ptr unsafe.Pointer, iter *jsoniter.Iterator) {if iter.WhatIsNext() == jsoniter.StringValue {str := iter.ReadString()if _, err := strconv.Atoi(str); err == nil{newIter := iter.Pool().BorrowIterator([]byte(str))defer iter.Pool().ReturnIterator(newIter)codec.originDecoder.Decode(ptr, newIter)}else{codec.originDecoder.Decode(ptr, iter)}} else {codec.originDecoder.Decode(ptr, iter)}}func (e *decorateExtension) DecorateDecoder(typ reflect2.Type, decoder jsoniter.ValDecoder) jsoniter.ValDecoder{if typ.Kind() == reflect.Int {return &decorateCodec{decoder}}return nil}func extensionTest(){var i intjsoniter.RegisterExtension(&sampleExtension{})jsoniter.RegisterExtension(&decorateExtension{})jsoniter.UnmarshalFromString(`30000`, &i)fmt.Println(i)// Output:// 29000jsoniter.UnmarshalFromString(`"40000"`, &i)fmt.Println(i)// Output:// 39000}type decorateExtension struct{ jsoniter.DummyExtension } type decorateCodec struct{ originDecoder jsoniter.ValDecoder } func (codec *decorateCodec) Decode(ptr unsafe.Pointer, iter *jsoniter.Iterator) { if iter.WhatIsNext() == jsoniter.StringValue { str := iter.ReadString() if _, err := strconv.Atoi(str); err == nil{ newIter := iter.Pool().BorrowIterator([]byte(str)) defer iter.Pool().ReturnIterator(newIter) codec.originDecoder.Decode(ptr, newIter) }else{ codec.originDecoder.Decode(ptr, iter) } } else { codec.originDecoder.Decode(ptr, iter) } } func (e *decorateExtension) DecorateDecoder(typ reflect2.Type, decoder jsoniter.ValDecoder) jsoniter.ValDecoder{ if typ.Kind() == reflect.Int { return &decorateCodec{decoder} } return nil } func extensionTest(){ var i int jsoniter.RegisterExtension(&sampleExtension{}) jsoniter.RegisterExtension(&decorateExtension{}) jsoniter.UnmarshalFromString(`30000`, &i) fmt.Println(i) // Output: // 29000 jsoniter.UnmarshalFromString(`"40000"`, &i) fmt.Println(i) // Output: // 39000 }
type decorateExtension struct{ jsoniter.DummyExtension } type decorateCodec struct{ originDecoder jsoniter.ValDecoder } func (codec *decorateCodec) Decode(ptr unsafe.Pointer, iter *jsoniter.Iterator) { if iter.WhatIsNext() == jsoniter.StringValue { str := iter.ReadString() if _, err := strconv.Atoi(str); err == nil{ newIter := iter.Pool().BorrowIterator([]byte(str)) defer iter.Pool().ReturnIterator(newIter) codec.originDecoder.Decode(ptr, newIter) }else{ codec.originDecoder.Decode(ptr, iter) } } else { codec.originDecoder.Decode(ptr, iter) } } func (e *decorateExtension) DecorateDecoder(typ reflect2.Type, decoder jsoniter.ValDecoder) jsoniter.ValDecoder{ if typ.Kind() == reflect.Int { return &decorateCodec{decoder} } return nil } func extensionTest(){ var i int jsoniter.RegisterExtension(&sampleExtension{}) jsoniter.RegisterExtension(&decorateExtension{}) jsoniter.UnmarshalFromString(`30000`, &i) fmt.Println(i) // Output: // 29000 jsoniter.UnmarshalFromString(`"40000"`, &i) fmt.Println(i) // Output: // 39000 }
在
CreateDecoder
和CreateEncoder
的例子基础上,我们在注册一个Extension
,这个Extension
只实现了装饰器功能,它兼容字符串类型数字的解析,并且解析出来的数字依然要-1000再赋值
3.3、作用域
json-iterator有两个RegisterExtension
接口可以调用,一个是package级别的jsoniter.RegisterExtension
,一个是API
(说明见Config章节)级别的API.RegisterExtension
。这两个函数都可以用来注册扩展,但是两种注册方式注册的扩展的作用域略有不同。jsoniter.RegisterExtension
注册的扩展,对于所有Config
生成的API
都生效;而API.RegisterExtension
只对其对应的Config
生成的API
接口生效,这个需要注意
4、Any(重点)
很多情况下,对于一个json输入源,我们只对其部分内容感兴趣。为了得到我们需要的小部分信息,去定义跟这个json串匹配的schema是一件麻烦的事件。对于体积庞大或者嵌套层次深的json串尤其如此。json-iterator提供了Any
对象,可以很方便地从json串中获取你想要的元素,而不需要去定义schema
4.1、使用简单
假设我们有这么一个json
jsonStr := []byte(`{"users": [{"username": "system","avatar_template": "/user_avatar/discourse.metabase.com/system/{size}/6_1.png","id": -1},{"username": "zergot","avatar_template": "https://avatars.discourse.org/v2/letter/z/0ea827/{size}.png","id": 89}],"topics": {"can_create_topic": false,"more_topics_url": "/c/uncategorized/l/latest?page=1","draft": null,"draft_key": "new_topic","draft_sequence": null,"per_page": 30,"topics": [{"bumped": true,"id": 8,"excerpt": "Welcome to Metabase\u0026#39;s discussion forum. This is a place to get help on installation, setting up as well as sharing tips and tricks.","category_id": 1,"unseen": false,"slug": "welcome-to-metabases-discussion-forum","fancy_title": "Welcome to Metabase\u0026rsquo;s Discussion Forum","bookmarked": null,"archived": false,"archetype": "regular","highest_post_number": 1,"reply_count": 0,"visible": true,"closed": false,"liked": null,"posts_count": 1,"views": 197,"image_url": "/images/welcome/discourse-edit-post-animated.gif","created_at": "2015-10-17T00:14:49.526Z","last_posted_at": "2015-10-17T00:14:49.557Z","pinned": true,"title": "Welcome to Metabase's Discussion Forum","has_summary": false,"like_count": 0,"pinned_globally": true,"last_poster_username": "system","posters": [{"extras": "latest single","description": "Original Poster, Most Recent Poster", // 我们需要这个"user_id": -1}],"bumped_at": "2015-10-21T02:32:22.486Z","unpinned": null}]}}`)jsonStr := []byte(`{ "users": [ { "username": "system", "avatar_template": "/user_avatar/discourse.metabase.com/system/{size}/6_1.png", "id": -1 }, { "username": "zergot", "avatar_template": "https://avatars.discourse.org/v2/letter/z/0ea827/{size}.png", "id": 89 } ], "topics": { "can_create_topic": false, "more_topics_url": "/c/uncategorized/l/latest?page=1", "draft": null, "draft_key": "new_topic", "draft_sequence": null, "per_page": 30, "topics": [ { "bumped": true, "id": 8, "excerpt": "Welcome to Metabase\u0026#39;s discussion forum. This is a place to get help on installation, setting up as well as sharing tips and tricks.", "category_id": 1, "unseen": false, "slug": "welcome-to-metabases-discussion-forum", "fancy_title": "Welcome to Metabase\u0026rsquo;s Discussion Forum", "bookmarked": null, "archived": false, "archetype": "regular", "highest_post_number": 1, "reply_count": 0, "visible": true, "closed": false, "liked": null, "posts_count": 1, "views": 197, "image_url": "/images/welcome/discourse-edit-post-animated.gif", "created_at": "2015-10-17T00:14:49.526Z", "last_posted_at": "2015-10-17T00:14:49.557Z", "pinned": true, "title": "Welcome to Metabase's Discussion Forum", "has_summary": false, "like_count": 0, "pinned_globally": true, "last_poster_username": "system", "posters": [ { "extras": "latest single", "description": "Original Poster, Most Recent Poster", // 我们需要这个 "user_id": -1 } ], "bumped_at": "2015-10-21T02:32:22.486Z", "unpinned": null } ] } }`)jsonStr := []byte(`{ "users": [ { "username": "system", "avatar_template": "/user_avatar/discourse.metabase.com/system/{size}/6_1.png", "id": -1 }, { "username": "zergot", "avatar_template": "https://avatars.discourse.org/v2/letter/z/0ea827/{size}.png", "id": 89 } ], "topics": { "can_create_topic": false, "more_topics_url": "/c/uncategorized/l/latest?page=1", "draft": null, "draft_key": "new_topic", "draft_sequence": null, "per_page": 30, "topics": [ { "bumped": true, "id": 8, "excerpt": "Welcome to Metabase\u0026#39;s discussion forum. This is a place to get help on installation, setting up as well as sharing tips and tricks.", "category_id": 1, "unseen": false, "slug": "welcome-to-metabases-discussion-forum", "fancy_title": "Welcome to Metabase\u0026rsquo;s Discussion Forum", "bookmarked": null, "archived": false, "archetype": "regular", "highest_post_number": 1, "reply_count": 0, "visible": true, "closed": false, "liked": null, "posts_count": 1, "views": 197, "image_url": "/images/welcome/discourse-edit-post-animated.gif", "created_at": "2015-10-17T00:14:49.526Z", "last_posted_at": "2015-10-17T00:14:49.557Z", "pinned": true, "title": "Welcome to Metabase's Discussion Forum", "has_summary": false, "like_count": 0, "pinned_globally": true, "last_poster_username": "system", "posters": [ { "extras": "latest single", "description": "Original Poster, Most Recent Poster", // 我们需要这个 "user_id": -1 } ], "bumped_at": "2015-10-21T02:32:22.486Z", "unpinned": null } ] } }`)
如果用传统的方法,那么首先我们应该先定义一个匹配这个json结构的结构体,然后调用Unmarshal
来反序列化,再获取这个结构体中我们需要的字段的值。如果用Any
,那么就很简单了:
any := jsoniter.Get(jsonStr, "topics", "topics", 0, "posters", 0, "description")fmt.Println(any.ToString())// Output:// Original Poster, Most Recent Posterany := jsoniter.Get(jsonStr, "topics", "topics", 0, "posters", 0, "description") fmt.Println(any.ToString()) // Output: // Original Poster, Most Recent Posterany := jsoniter.Get(jsonStr, "topics", "topics", 0, "posters", 0, "description") fmt.Println(any.ToString()) // Output: // Original Poster, Most Recent Poster
只需要一行,我们就可以拿到我们想要的元素。然后调用Any
对象提供的接口做下转换,就得到了我们要的description字符串
4.2、与schema结合
还是上面的例子
jsonStr := []byte(`{"users": [{"username": "system","avatar_template": "/user_avatar/discourse.metabase.com/system/{size}/6_1.png","id": -1},{"username": "zergot","avatar_template": "https://avatars.discourse.org/v2/letter/z/0ea827/{size}.png","id": 89}],"topics": {"can_create_topic": false,"more_topics_url": "/c/uncategorized/l/latest?page=1","draft": null,"draft_key": "new_topic","draft_sequence": null,"per_page": 30,"topics": [{"bumped": true,"id": 8,"excerpt": "Welcome to Metabase\u0026#39;s discussion forum. This is a place to get help on installation, setting up as well as sharing tips and tricks.","category_id": 1,"unseen": false,"slug": "welcome-to-metabases-discussion-forum","fancy_title": "Welcome to Metabase\u0026rsquo;s Discussion Forum","bookmarked": null,"archived": false,"archetype": "regular","highest_post_number": 1,"reply_count": 0,"visible": true,"closed": false,"liked": null,"posts_count": 1,"views": 197,"image_url": "/images/welcome/discourse-edit-post-animated.gif","created_at": "2015-10-17T00:14:49.526Z","last_posted_at": "2015-10-17T00:14:49.557Z","pinned": true,"title": "Welcome to Metabase's Discussion Forum","has_summary": false,"like_count": 0,"pinned_globally": true,"last_poster_username": "system","posters": [{ // 这次我们需要这个"extras": "latest single","description": "Original Poster, Most Recent Poster","user_id": -1}],"bumped_at": "2015-10-21T02:32:22.486Z","unpinned": null}]}}`)jsonStr := []byte(`{ "users": [ { "username": "system", "avatar_template": "/user_avatar/discourse.metabase.com/system/{size}/6_1.png", "id": -1 }, { "username": "zergot", "avatar_template": "https://avatars.discourse.org/v2/letter/z/0ea827/{size}.png", "id": 89 } ], "topics": { "can_create_topic": false, "more_topics_url": "/c/uncategorized/l/latest?page=1", "draft": null, "draft_key": "new_topic", "draft_sequence": null, "per_page": 30, "topics": [ { "bumped": true, "id": 8, "excerpt": "Welcome to Metabase\u0026#39;s discussion forum. This is a place to get help on installation, setting up as well as sharing tips and tricks.", "category_id": 1, "unseen": false, "slug": "welcome-to-metabases-discussion-forum", "fancy_title": "Welcome to Metabase\u0026rsquo;s Discussion Forum", "bookmarked": null, "archived": false, "archetype": "regular", "highest_post_number": 1, "reply_count": 0, "visible": true, "closed": false, "liked": null, "posts_count": 1, "views": 197, "image_url": "/images/welcome/discourse-edit-post-animated.gif", "created_at": "2015-10-17T00:14:49.526Z", "last_posted_at": "2015-10-17T00:14:49.557Z", "pinned": true, "title": "Welcome to Metabase's Discussion Forum", "has_summary": false, "like_count": 0, "pinned_globally": true, "last_poster_username": "system", "posters": [ { // 这次我们需要这个 "extras": "latest single", "description": "Original Poster, Most Recent Poster", "user_id": -1 } ], "bumped_at": "2015-10-21T02:32:22.486Z", "unpinned": null } ] } }`)jsonStr := []byte(`{ "users": [ { "username": "system", "avatar_template": "/user_avatar/discourse.metabase.com/system/{size}/6_1.png", "id": -1 }, { "username": "zergot", "avatar_template": "https://avatars.discourse.org/v2/letter/z/0ea827/{size}.png", "id": 89 } ], "topics": { "can_create_topic": false, "more_topics_url": "/c/uncategorized/l/latest?page=1", "draft": null, "draft_key": "new_topic", "draft_sequence": null, "per_page": 30, "topics": [ { "bumped": true, "id": 8, "excerpt": "Welcome to Metabase\u0026#39;s discussion forum. This is a place to get help on installation, setting up as well as sharing tips and tricks.", "category_id": 1, "unseen": false, "slug": "welcome-to-metabases-discussion-forum", "fancy_title": "Welcome to Metabase\u0026rsquo;s Discussion Forum", "bookmarked": null, "archived": false, "archetype": "regular", "highest_post_number": 1, "reply_count": 0, "visible": true, "closed": false, "liked": null, "posts_count": 1, "views": 197, "image_url": "/images/welcome/discourse-edit-post-animated.gif", "created_at": "2015-10-17T00:14:49.526Z", "last_posted_at": "2015-10-17T00:14:49.557Z", "pinned": true, "title": "Welcome to Metabase's Discussion Forum", "has_summary": false, "like_count": 0, "pinned_globally": true, "last_poster_username": "system", "posters": [ { // 这次我们需要这个 "extras": "latest single", "description": "Original Poster, Most Recent Poster", "user_id": -1 } ], "bumped_at": "2015-10-21T02:32:22.486Z", "unpinned": null } ] } }`)
这次我们需要”posters”数组的第一个结构体,我们现在已经有它的schema定义了,除此之外这个json的其他信息我都不需要,那么如何通过Any
对象获得这个结构体呢?我们需要ToVal
接口:
type Poster struct {Extras string `json:"extras"`Desc string `json:"description"`UserId int `json:"user_id"`}var p Posterany := jsoniter.Get(jsonStr, "topics", "topics", 0, "posters", 0)any.ToVal(&p)fmt.Printf("extras=%s\ndescription=%s\nuser_id=%d\n", p.Extras, p.Desc, p.UserId)// Output:// extras=latest single// description=Original Poster, Most Recent Poster// user_id=-1type Poster struct { Extras string `json:"extras"` Desc string `json:"description"` UserId int `json:"user_id"` } var p Poster any := jsoniter.Get(jsonStr, "topics", "topics", 0, "posters", 0) any.ToVal(&p) fmt.Printf("extras=%s\ndescription=%s\nuser_id=%d\n", p.Extras, p.Desc, p.UserId) // Output: // extras=latest single // description=Original Poster, Most Recent Poster // user_id=-1type Poster struct { Extras string `json:"extras"` Desc string `json:"description"` UserId int `json:"user_id"` } var p Poster any := jsoniter.Get(jsonStr, "topics", "topics", 0, "posters", 0) any.ToVal(&p) fmt.Printf("extras=%s\ndescription=%s\nuser_id=%d\n", p.Extras, p.Desc, p.UserId) // Output: // extras=latest single // description=Original Poster, Most Recent Poster // user_id=-1
这里可以看到,首先我们拿到了”posters”第一个元素的Any
对象,然后调用ToVal
方法,就可以像之前的反序列化方法一样把数据解析出来。实际上,如果你的Any
对象对应的是数组或对象类型的元素,它内部保存了这个元素原始的json串。当你需要获取其字段、元素或者将其反序列化出来的时候,才会触发解析。json-iterator内部将其称为懒解析。来看个数组的例子:
type User struct {UserName string `json:"username"`Template string `json:"avatar_template"`Id int `json:"id"`}var users []Userany := jsoniter.Get(jsonStr, "users")fmt.Println(any.Get(0, "username").ToString())// Output:// systemany.ToVal(&users)fmt.Printf("username=%s\navatar_template=%s\nid=%d\n", users[1].UserName, users[1].Template, users[1].Id)// Output:// username=zergot// avatar_template=https://avatars.discourse.org/v2/letter/z/0ea827/{size}.png// id=89type User struct { UserName string `json:"username"` Template string `json:"avatar_template"` Id int `json:"id"` } var users []User any := jsoniter.Get(jsonStr, "users") fmt.Println(any.Get(0, "username").ToString()) // Output: // system any.ToVal(&users) fmt.Printf("username=%s\navatar_template=%s\nid=%d\n", users[1].UserName, users[1].Template, users[1].Id) // Output: // username=zergot // avatar_template=https://avatars.discourse.org/v2/letter/z/0ea827/{size}.png // id=89type User struct { UserName string `json:"username"` Template string `json:"avatar_template"` Id int `json:"id"` } var users []User any := jsoniter.Get(jsonStr, "users") fmt.Println(any.Get(0, "username").ToString()) // Output: // system any.ToVal(&users) fmt.Printf("username=%s\navatar_template=%s\nid=%d\n", users[1].UserName, users[1].Template, users[1].Id) // Output: // username=zergot // avatar_template=https://avatars.discourse.org/v2/letter/z/0ea827/{size}.png // id=89
数组元素的获取方法其实也是类似,这里不再详述。
有一点需要说明的是,只有数组和对象的json元素对应的Any
才提供ToVal
方法,也就是说这两种json元素的Any
对象才实现了懒解析,其他诸如int
,bool
,string
等都没有实现,实际上它们也不需要什么懒解析
5、Iterator
json-iterator中使用Iterator
来实现流式解析。通过其提供的API,我们可以控制json串的解析行为,我们可以对json串中与schema定义不一致的字段做兼容性的解析处理,也可以跳过我们不关心的json串中的片段
5.1、创建Iterator实例
有三种方法可以创建Iterator
实例:
-
从
API
对象的Iterator
实例池中Borrow一个c := jsoniter.ConfigDefaulti := c.BorrowIterator([]byte(`{"A":"a"}`))defer c.ReturnIterator(i)// 你的功能实现// xxxxxx// ......c := jsoniter.ConfigDefault i := c.BorrowIterator([]byte(`{"A":"a"}`)) defer c.ReturnIterator(i) // 你的功能实现 // xxxxxx // ......
c := jsoniter.ConfigDefault i := c.BorrowIterator([]byte(`{"A":"a"}`)) defer c.ReturnIterator(i) // 你的功能实现 // xxxxxx // ......
使用这种方法”借用”的
Iterator
实例,记得在使用完毕后”返还”回去 -
调用
NewIterator
接口新建一个i := jsoniter.NewIterator(jsoniter.ConfigDefault)i.Reset(os.Stdin)// 或者i.ResetBytes(`{"A":"a"}`)// 你的功能实现// xxxxxx// ......i := jsoniter.NewIterator(jsoniter.ConfigDefault) i.Reset(os.Stdin) // 或者i.ResetBytes(`{"A":"a"}`) // 你的功能实现 // xxxxxx // ......
i := jsoniter.NewIterator(jsoniter.ConfigDefault) i.Reset(os.Stdin) // 或者i.ResetBytes(`{"A":"a"}`) // 你的功能实现 // xxxxxx // ......
使用这种方法,需要传入你的序列化配置对应生成的
API
对象。对于这种方法,要指定输入源io.Reader
或输入json串都只能在创建了Iterator
后,调用其重置方法Reset
或ResetBytes
来设置其待解析输入。如果要在创建的时候就指定输入源,可以用第三种方法 -
调用
ParseXXX
方法新建一个i := jsoniter.Parse(jsoniter.ConfigDefault, os.Stdin, 1024)// 或者 i := jsoniter.ParseBytes(jsoniter.ConfigDefault, []byte(`{"A":"a"}`))// 或者 i := jsoniter.ParseString(jsoniter.ConfigDefault, `{"A":"a"}`)// 你的功能实现// xxxxxx// ......i := jsoniter.Parse(jsoniter.ConfigDefault, os.Stdin, 1024) // 或者 i := jsoniter.ParseBytes(jsoniter.ConfigDefault, []byte(`{"A":"a"}`)) // 或者 i := jsoniter.ParseString(jsoniter.ConfigDefault, `{"A":"a"}`) // 你的功能实现 // xxxxxx // ......
i := jsoniter.Parse(jsoniter.ConfigDefault, os.Stdin, 1024) // 或者 i := jsoniter.ParseBytes(jsoniter.ConfigDefault, []byte(`{"A":"a"}`)) // 或者 i := jsoniter.ParseString(jsoniter.ConfigDefault, `{"A":"a"}`) // 你的功能实现 // xxxxxx // ......
使用
Parse
族的方法,可以在创建Iterator
的时候指定待解析json串的输入源。其中Parse
方法还可以指定Iterator
用于解析的内部缓冲的大小
5.2、定制解析行为
想象一个这样的场景:我们的数据结构schema中某个字段定义成了bool
类型,但是我们接收到的json串中,该字段对应的值可能是bool
类型,可能是int
类型,还可能是string
类型,我们需要对其做兼容性的解析处理,这时候Iterator
(配合Extension
或ValDecoder
)就可以发挥作用了。
type testStructForIterator struct{BoolField bool}jsoniter.RegisterFieldDecoder(reflect2.TypeOf(testStructForIterator{}).String(), "BoolField",&wrapDecoder{func(ptr unsafe.Pointer, iter *jsoniter.Iterator) {typ := iter.WhatIsNext()switch typ {case jsoniter.BoolValue:*((*bool)(ptr)) = iter.ReadBool()case jsoniter.NumberValue:number := iter.ReadNumber()if n, err := number.Int64(); err == nil{if n > 0{*((*bool)(ptr)) = true}else{*((*bool)(ptr)) = false}}else{*((*bool)(ptr)) = false}case jsoniter.StringValue:str := iter.ReadString()if str == "true"{*((*bool)(ptr)) = true}else{*((*bool)(ptr)) = false}case jsoniter.NilValue:iter.ReadNil()*((*bool)(ptr)) = falsedefault:iter.ReportError("wrapDecoder", "unknown value type")}},})t := testStructForIterator{}if err := jsoniter.Unmarshal([]byte(`{"BoolField":true}`), &t); err == nil{fmt.Println(t.BoolField)// 输出:true}if err := jsoniter.Unmarshal([]byte(`{"BoolField":1}`), &t); err == nil{fmt.Println(t.BoolField)// 输出:true}if err := jsoniter.Unmarshal([]byte(`{"BoolField":"true"}`), &t); err == nil{fmt.Println(t.BoolField)// 输出:true}if err := jsoniter.Unmarshal([]byte(`{"BoolField":"false"}`), &t); err == nil{fmt.Println(t.BoolField)// 输出:false}if err := jsoniter.Unmarshal([]byte(`{"BoolField":null}`), &t); err == nil{fmt.Println(t.BoolField)// 输出:false}type testStructForIterator struct{ BoolField bool } jsoniter.RegisterFieldDecoder(reflect2.TypeOf(testStructForIterator{}).String(), "BoolField", &wrapDecoder{ func(ptr unsafe.Pointer, iter *jsoniter.Iterator) { typ := iter.WhatIsNext() switch typ { case jsoniter.BoolValue: *((*bool)(ptr)) = iter.ReadBool() case jsoniter.NumberValue: number := iter.ReadNumber() if n, err := number.Int64(); err == nil{ if n > 0{ *((*bool)(ptr)) = true }else{ *((*bool)(ptr)) = false } }else{ *((*bool)(ptr)) = false } case jsoniter.StringValue: str := iter.ReadString() if str == "true"{ *((*bool)(ptr)) = true }else{ *((*bool)(ptr)) = false } case jsoniter.NilValue: iter.ReadNil() *((*bool)(ptr)) = false default: iter.ReportError("wrapDecoder", "unknown value type") } }, }) t := testStructForIterator{} if err := jsoniter.Unmarshal([]byte(`{"BoolField":true}`), &t); err == nil{ fmt.Println(t.BoolField) // 输出:true } if err := jsoniter.Unmarshal([]byte(`{"BoolField":1}`), &t); err == nil{ fmt.Println(t.BoolField) // 输出:true } if err := jsoniter.Unmarshal([]byte(`{"BoolField":"true"}`), &t); err == nil{ fmt.Println(t.BoolField) // 输出:true } if err := jsoniter.Unmarshal([]byte(`{"BoolField":"false"}`), &t); err == nil{ fmt.Println(t.BoolField) // 输出:false } if err := jsoniter.Unmarshal([]byte(`{"BoolField":null}`), &t); err == nil{ fmt.Println(t.BoolField) // 输出:false }type testStructForIterator struct{ BoolField bool } jsoniter.RegisterFieldDecoder(reflect2.TypeOf(testStructForIterator{}).String(), "BoolField", &wrapDecoder{ func(ptr unsafe.Pointer, iter *jsoniter.Iterator) { typ := iter.WhatIsNext() switch typ { case jsoniter.BoolValue: *((*bool)(ptr)) = iter.ReadBool() case jsoniter.NumberValue: number := iter.ReadNumber() if n, err := number.Int64(); err == nil{ if n > 0{ *((*bool)(ptr)) = true }else{ *((*bool)(ptr)) = false } }else{ *((*bool)(ptr)) = false } case jsoniter.StringValue: str := iter.ReadString() if str == "true"{ *((*bool)(ptr)) = true }else{ *((*bool)(ptr)) = false } case jsoniter.NilValue: iter.ReadNil() *((*bool)(ptr)) = false default: iter.ReportError("wrapDecoder", "unknown value type") } }, }) t := testStructForIterator{} if err := jsoniter.Unmarshal([]byte(`{"BoolField":true}`), &t); err == nil{ fmt.Println(t.BoolField) // 输出:true } if err := jsoniter.Unmarshal([]byte(`{"BoolField":1}`), &t); err == nil{ fmt.Println(t.BoolField) // 输出:true } if err := jsoniter.Unmarshal([]byte(`{"BoolField":"true"}`), &t); err == nil{ fmt.Println(t.BoolField) // 输出:true } if err := jsoniter.Unmarshal([]byte(`{"BoolField":"false"}`), &t); err == nil{ fmt.Println(t.BoolField) // 输出:false } if err := jsoniter.Unmarshal([]byte(`{"BoolField":null}`), &t); err == nil{ fmt.Println(t.BoolField) // 输出:false }
在上面这个例子里面,我们针对testStructForIterator
的BoolField
字段注册了一个ValDecoder
。在它的Decode
方法中,我们先调用Iterator
的WhatIsNext
方法,通过json串中下一个元素的类似,来决定调用Iterator
的哪个方法来解析下一个数值,根据解析结果,设置ptr
指向的bool
类型的数据值。这样不管我们解析的json串中,BoolField
字段实际使用布尔、数值或是字符串来表示,我们都可以做到兼容
Iterator
开放了各种接口用于从输入中读入不同类型的数据:
ReadBool
ReadString
ReadInt
ReadFloat32
ReadMapCB
ReadObjectCB
ReadArrayCB
- ……
具体每个方法的说明可以参考godoc
5.3、跳过json片段
使用Iterator
,我们可以跳过json串中的特定片段,只处理我们感兴趣的部分。考虑这么一个场景:我们接收到一个json串,这个json串中包含了一个对象,我们只想把这个对象的每个字段的字段名记录下来,至于字段对应的具体内容,我们不关心。为了实现这样的需求,我们需要用到Iterator
jsonStr := `{"_id": "58451574858913704731","about": "a4KzKZRVvqfBLdnpUWaD","address": "U2YC2AEVn8ab4InRwDmu","age": 27,"balance": "I5cZ5vRPmVXW0lhhRzF4","company": "jwLot8sFN1hMdE4EVW7e","email": "30KqJ0oeYXLqhKMLDUg6","eyeColor": "RWXrMsO6xi9cpxPqzJA1","favoriteFruit": "iyOuAekbybTUeDJqkHNI","gender": "ytgB3Kzoejv1FGU6biXu","greeting": "7GXmN2vMLcS2uimxGQgC","guid": "bIqNIywgrzva4d5LfNlm","index": 169390966,"isActive": true,"latitude": 70.7333712683406,"longitude": 16.25873969455544,"name": "bvtukpT6dXtqfbObGyBU","phone": "UsxtI7sWGIEGvM2N1Mh0","picture": "8fiyZ2oKapWtH5kXyNDZJjvRS5PGzJGGxDCAk1he1wuhUjxfjtGIh6agQMbjovF10YlqOyzhQPCagBZpW41r6CdrghVfgtpDy7YH","registered": "gJDieuwVu9H7eYmYnZkz","tags": ["M2b9n0QrqC","zl6iJcT68v","VRuP4BRWjs","ZY9jXIjTMR"]}`fieldList := make([]string, 0)iter := jsoniter.ParseString(jsoniter.ConfigDefault, jsonStr)iter.ReadObjectCB(func(iter *jsoniter.Iterator, field string) bool{fieldList = append(fieldList, field)iter.Skip()return true})fmt.Println(fieldList)// 输出:[_id about address age balance company email eyeColor favoriteFruit gender greeting guid index isActive latitude longitude name phone picture registered tags]jsonStr := ` { "_id": "58451574858913704731", "about": "a4KzKZRVvqfBLdnpUWaD", "address": "U2YC2AEVn8ab4InRwDmu", "age": 27, "balance": "I5cZ5vRPmVXW0lhhRzF4", "company": "jwLot8sFN1hMdE4EVW7e", "email": "30KqJ0oeYXLqhKMLDUg6", "eyeColor": "RWXrMsO6xi9cpxPqzJA1", "favoriteFruit": "iyOuAekbybTUeDJqkHNI", "gender": "ytgB3Kzoejv1FGU6biXu", "greeting": "7GXmN2vMLcS2uimxGQgC", "guid": "bIqNIywgrzva4d5LfNlm", "index": 169390966, "isActive": true, "latitude": 70.7333712683406, "longitude": 16.25873969455544, "name": "bvtukpT6dXtqfbObGyBU", "phone": "UsxtI7sWGIEGvM2N1Mh0", "picture": "8fiyZ2oKapWtH5kXyNDZJjvRS5PGzJGGxDCAk1he1wuhUjxfjtGIh6agQMbjovF10YlqOyzhQPCagBZpW41r6CdrghVfgtpDy7YH", "registered": "gJDieuwVu9H7eYmYnZkz", "tags": [ "M2b9n0QrqC", "zl6iJcT68v", "VRuP4BRWjs", "ZY9jXIjTMR" ] } ` fieldList := make([]string, 0) iter := jsoniter.ParseString(jsoniter.ConfigDefault, jsonStr) iter.ReadObjectCB(func(iter *jsoniter.Iterator, field string) bool{ fieldList = append(fieldList, field) iter.Skip() return true }) fmt.Println(fieldList) // 输出:[_id about address age balance company email eyeColor favoriteFruit gender greeting guid index isActive latitude longitude name phone picture registered tags]jsonStr := ` { "_id": "58451574858913704731", "about": "a4KzKZRVvqfBLdnpUWaD", "address": "U2YC2AEVn8ab4InRwDmu", "age": 27, "balance": "I5cZ5vRPmVXW0lhhRzF4", "company": "jwLot8sFN1hMdE4EVW7e", "email": "30KqJ0oeYXLqhKMLDUg6", "eyeColor": "RWXrMsO6xi9cpxPqzJA1", "favoriteFruit": "iyOuAekbybTUeDJqkHNI", "gender": "ytgB3Kzoejv1FGU6biXu", "greeting": "7GXmN2vMLcS2uimxGQgC", "guid": "bIqNIywgrzva4d5LfNlm", "index": 169390966, "isActive": true, "latitude": 70.7333712683406, "longitude": 16.25873969455544, "name": "bvtukpT6dXtqfbObGyBU", "phone": "UsxtI7sWGIEGvM2N1Mh0", "picture": "8fiyZ2oKapWtH5kXyNDZJjvRS5PGzJGGxDCAk1he1wuhUjxfjtGIh6agQMbjovF10YlqOyzhQPCagBZpW41r6CdrghVfgtpDy7YH", "registered": "gJDieuwVu9H7eYmYnZkz", "tags": [ "M2b9n0QrqC", "zl6iJcT68v", "VRuP4BRWjs", "ZY9jXIjTMR" ] } ` fieldList := make([]string, 0) iter := jsoniter.ParseString(jsoniter.ConfigDefault, jsonStr) iter.ReadObjectCB(func(iter *jsoniter.Iterator, field string) bool{ fieldList = append(fieldList, field) iter.Skip() return true }) fmt.Println(fieldList) // 输出:[_id about address age balance company email eyeColor favoriteFruit gender greeting guid index isActive latitude longitude name phone picture registered tags]
在上面的例子中,我们调用了ParseString
来创建一个Iterator
实例。ParseString
可以指定Iterator
实例对应的配置和作为解析源的json串。然后我们调用了Iterator
的ReadObjectCB
方法,调用时必须传入一个回调函数。ReadObjectCB
方法会解析一个对象类型的json串,并迭代这个json串中的顶层对象的每个字段,对每个字段都会调用我们一开始传进去的回调函数。这里可以看到,在回调函数里面,我们只是将传进来的字段名记录下来,然后调用Iterator
的Skip
来跳过这个字段对应的实际内容。Skip
会自动解析json串中接下来的元素是什么类型的,然后跳过它的解析,跳到下一个字段。当遍历完毕后我们就可以拿到我们需要的字段列表了。
5.4、另一种反序列化接口
Iterator
也提供了一个接口,可以实现跟Decoder
的Decode
方法基本一样的序列化功能
type testStructForIterator struct{Name stringId int}var dat testStructForIteratoriter := jsoniter.Parse(jsoniter.ConfigDefault, nil, 1024)iter.ResetBytes([]byte(`{"Name":"Allen","Id":100}`))if iter.ReadVal(&dat); iter.Error == nil || iter.Error == io.EOF{fmt.Println(dat)// 输出:{Allen 100}}type testStructForIterator struct{ Name string Id int } var dat testStructForIterator iter := jsoniter.Parse(jsoniter.ConfigDefault, nil, 1024) iter.ResetBytes([]byte(`{"Name":"Allen","Id":100}`)) if iter.ReadVal(&dat); iter.Error == nil || iter.Error == io.EOF{ fmt.Println(dat) // 输出:{Allen 100} }type testStructForIterator struct{ Name string Id int } var dat testStructForIterator iter := jsoniter.Parse(jsoniter.ConfigDefault, nil, 1024) iter.ResetBytes([]byte(`{"Name":"Allen","Id":100}`)) if iter.ReadVal(&dat); iter.Error == nil || iter.Error == io.EOF{ fmt.Println(dat) // 输出:{Allen 100} }
在上面这个例子里面,我们调用Parse
来创建了一个Iterator
实例,不设置输入设备io.Reader
,我们用ResetBytes
来设置待解析的json串,然后调用ReadVal
方法来实现序列化。通过这种方式,也可以完成反序列化。实际上,json-iterator内部也是使用类似的方式,调用Iterator
的ReadVal
来完成反序列化。这里有一点需要说明:
- 调用
Parse
创建Iterator
实例,可以指定Iterator
内部缓冲的大小。对于解析输入源从io.Reader
读入的应用场合,由于Iterator
的内部流式实现,是不会一次过将数据从io.Reader
全部读取出来然后解析的,而是每次读入不超过缓冲区长度的大小的数据,然后解析。当解析过程发现缓冲区中数据已经解析完,又会从io.Reader
中读取数据到缓冲区,继续解析,直至整个完整的json串解析完毕。考虑这么一个例子:你的Iterator
的缓冲区大小设置为1024,但你的io.Reader
里面有10M的json串需要解析,这样大概可以认为要把这个json串解析完,需要从io.Reader
读入数据10240次,每次读1024字节。因此,如果你的解析源需要从io.Reader
中读入,对性能要求较高,而对内存占用不太敏感,那么不妨放弃直接调用Unmarshal
,自己创建Iterator
来进行反序列化,并适当将Iterator
的缓冲设置得大一点,提高解析效率
5.5、复用Iterator实例
你可以调用Reset
(解析源为io.Reader
)或者ResetBytes
(解析源为字符串或字节序列)来复用你的Iterator
实例
type testStructForIterator struct{Name stringId int}var dat testStructForIteratoriter := jsoniter.ParseString(jsoniter.ConfigDefault, `{"Name":"Allen","Id":100}`)iter.ReadVal(&dat)// xxxxxx// ......if iter.Error != nil{return}iter.ResetBytes([]byte(`{"Name":"Tom","Id":200}`))iter.ReadVal(&dat)type testStructForIterator struct{ Name string Id int } var dat testStructForIterator iter := jsoniter.ParseString(jsoniter.ConfigDefault, `{"Name":"Allen","Id":100}`) iter.ReadVal(&dat) // xxxxxx // ...... if iter.Error != nil{ return } iter.ResetBytes([]byte(`{"Name":"Tom","Id":200}`)) iter.ReadVal(&dat)type testStructForIterator struct{ Name string Id int } var dat testStructForIterator iter := jsoniter.ParseString(jsoniter.ConfigDefault, `{"Name":"Allen","Id":100}`) iter.ReadVal(&dat) // xxxxxx // ...... if iter.Error != nil{ return } iter.ResetBytes([]byte(`{"Name":"Tom","Id":200}`)) iter.ReadVal(&dat)
请注意,如果你的Iterator
在反序列化过程中出现了错误,即Iterator.Error
不为nil,那么你不能继续使用这个Iterator
实例进行新的反序列化或解码,即使你调了Reset
/ResetBytes
进行重置也不行,只能重新另外创建一个新的Iterator
来使用(至少目前的实现必须这样)
6、Stream
json-iterator中使用Stream
来控制json的编码输出,通过其提供的API,配合自定义的Extension
或ValEncoder
,我们可以定制我们的数据如何编码输出成json,甚至可以从头构造并输出一个json串
6.1、创建Stream实例
有两种方法可以创建Stream
实例:
-
从
API
对象的Stream
实例池中Borrow一个c := jsoniter.ConfigDefaults := c.BorrowStream(os.Stdout)defer c.ReturnStream(s)// 你的功能实现// xxxxxx// ......c := jsoniter.ConfigDefault s := c.BorrowStream(os.Stdout) defer c.ReturnStream(s) // 你的功能实现 // xxxxxx // ......
c := jsoniter.ConfigDefault s := c.BorrowStream(os.Stdout) defer c.ReturnStream(s) // 你的功能实现 // xxxxxx // ......
使用这种方法”借用”的
Stream
实例,记得在使用完毕后”返还”回去 -
调用
NewStream
接口新建一个s := jsoniter.NewStream(jsoniter.ConfigDefault, os.Stdout, 1024)// 你的功能实现// xxxxxx// ......s := jsoniter.NewStream(jsoniter.ConfigDefault, os.Stdout, 1024) // 你的功能实现 // xxxxxx // ......
s := jsoniter.NewStream(jsoniter.ConfigDefault, os.Stdout, 1024) // 你的功能实现 // xxxxxx // ......
使用这种方法,需要传入你的序列化配置对应生成的
API
对象,底层输出的io.Writer
和指定Stream
的内部缓冲内部大小(见下文详述)
6.2、定制编码输出
在定义你的Extension
或ValEncoder
时,你需要用到Stream
来定制你的字段如何输出成json
type sampleExtension struct {jsoniter.DummyExtension}type wrapEncoder struct {encodeFunc func(ptr unsafe.Pointer, stream *jsoniter.Stream)isEmptyFunc func(ptr unsafe.Pointer) bool}func (enc *wrapEncoder) Encode(ptr unsafe.Pointer, stream *jsoniter.Stream) {enc.encodeFunc(ptr, stream)}func (enc *wrapEncoder) IsEmpty(ptr unsafe.Pointer) bool {if enc.isEmptyFunc == nil {return false}return enc.isEmptyFunc(ptr)}func (e *sampleExtension) CreateEncoder(typ reflect2.Type) jsoniter.ValEncoder {if typ.Kind() == reflect.Int {return &wrapEncoder{func(ptr unsafe.Pointer, stream *jsoniter.Stream) {// 将Int类型的变量的值+1000后,再写入到输出的jsonstream.WriteInt(*(*int)(ptr) + 1000)},nil,}}return nil}func streamTest(){jsoniter.RegisterExtension(&sampleExtension{})j, _ := jsoniter.MarshalToString(1000)fmt.Println(j)// 输出:2000}type sampleExtension struct { jsoniter.DummyExtension } type wrapEncoder struct { encodeFunc func(ptr unsafe.Pointer, stream *jsoniter.Stream) isEmptyFunc func(ptr unsafe.Pointer) bool } func (enc *wrapEncoder) Encode(ptr unsafe.Pointer, stream *jsoniter.Stream) { enc.encodeFunc(ptr, stream) } func (enc *wrapEncoder) IsEmpty(ptr unsafe.Pointer) bool { if enc.isEmptyFunc == nil { return false } return enc.isEmptyFunc(ptr) } func (e *sampleExtension) CreateEncoder(typ reflect2.Type) jsoniter.ValEncoder { if typ.Kind() == reflect.Int { return &wrapEncoder{ func(ptr unsafe.Pointer, stream *jsoniter.Stream) { // 将Int类型的变量的值+1000后,再写入到输出的json stream.WriteInt(*(*int)(ptr) + 1000) }, nil, } } return nil } func streamTest(){ jsoniter.RegisterExtension(&sampleExtension{}) j, _ := jsoniter.MarshalToString(1000) fmt.Println(j) // 输出:2000 }type sampleExtension struct { jsoniter.DummyExtension } type wrapEncoder struct { encodeFunc func(ptr unsafe.Pointer, stream *jsoniter.Stream) isEmptyFunc func(ptr unsafe.Pointer) bool } func (enc *wrapEncoder) Encode(ptr unsafe.Pointer, stream *jsoniter.Stream) { enc.encodeFunc(ptr, stream) } func (enc *wrapEncoder) IsEmpty(ptr unsafe.Pointer) bool { if enc.isEmptyFunc == nil { return false } return enc.isEmptyFunc(ptr) } func (e *sampleExtension) CreateEncoder(typ reflect2.Type) jsoniter.ValEncoder { if typ.Kind() == reflect.Int { return &wrapEncoder{ func(ptr unsafe.Pointer, stream *jsoniter.Stream) { // 将Int类型的变量的值+1000后,再写入到输出的json stream.WriteInt(*(*int)(ptr) + 1000) }, nil, } } return nil } func streamTest(){ jsoniter.RegisterExtension(&sampleExtension{}) j, _ := jsoniter.MarshalToString(1000) fmt.Println(j) // 输出:2000 }
在上面的例子中,我们注册了一个Extension
,这个Extension
的CreateEncoder
函数中,我们调用了Stream
的WriteInt
接口,来将ptr
指向的数值加1000后,再输出成json;在自定义ValEncoder
中,我们同样使用Stream
提供的函数来定制我们字段的输出
type testStructForStream struct{Field int}jsoniter.RegisterFieldEncoderFunc(reflect2.TypeOf(testStructForStream{}).String(), "Field",func(ptr unsafe.Pointer, stream *jsoniter.Stream) {// 将Int类型的值转换成字符串类型的json输出stream.WriteString(strconv.Itoa(*(*int)(ptr)))}, nil)j, _ := jsoniter.MarshalToString(testStructForStream{1024})fmt.Println(j)// 输出:{"Field":"1024"}type testStructForStream struct{ Field int } jsoniter.RegisterFieldEncoderFunc(reflect2.TypeOf(testStructForStream{}).String(), "Field", func(ptr unsafe.Pointer, stream *jsoniter.Stream) { // 将Int类型的值转换成字符串类型的json输出 stream.WriteString(strconv.Itoa(*(*int)(ptr))) }, nil) j, _ := jsoniter.MarshalToString(testStructForStream{1024}) fmt.Println(j) // 输出:{"Field":"1024"}type testStructForStream struct{ Field int } jsoniter.RegisterFieldEncoderFunc(reflect2.TypeOf(testStructForStream{}).String(), "Field", func(ptr unsafe.Pointer, stream *jsoniter.Stream) { // 将Int类型的值转换成字符串类型的json输出 stream.WriteString(strconv.Itoa(*(*int)(ptr))) }, nil) j, _ := jsoniter.MarshalToString(testStructForStream{1024}) fmt.Println(j) // 输出:{"Field":"1024"}
这个例子里面,我们针对testStructForStream
的Field
字段注册了一个ValEncoder
,这个ValEncoder
调用Stream
的WriteString
方法,将ptr
指向的Int
类型数值以字符串的方式写入到json串
Stream
开放了各种类型数据的写入方法,可以让我们很方便地去定制自己的数据以何种方式输出成json:
WriteBool
WriteInt
WriteFloat32
WriteString
WriteArrayStart
、WriteArrayEnd
WriteObjectStart
、WriteObjectEnd
WriteEmptyArray
WriteEmptyObject
- ……
具体每个方法的说明可以参考godoc
6.3、手动构造json输出
使用Stream
,可以完全手动地构造你的json如何输出成字节流
s := jsoniter.ConfigDefault.BorrowStream(nil)// 记得把从Config中borrow过来的Stream实例Return回去defer jsoniter.ConfigDefault.ReturnStream(s)s.WriteObjectStart()s.WriteObjectField("EmbedStruct")s.WriteObjectStart()s.WriteObjectField("Name")s.WriteString("xxx")s.WriteObjectEnd()s.WriteMore()s.WriteObjectField("Id")s.WriteInt(100)s.WriteObjectEnd()fmt.Println(string(s.Buffer()))// 输出:{"EmbedStruct":{"Name":"xxx"},"Id":100}s := jsoniter.ConfigDefault.BorrowStream(nil) // 记得把从Config中borrow过来的Stream实例Return回去 defer jsoniter.ConfigDefault.ReturnStream(s) s.WriteObjectStart() s.WriteObjectField("EmbedStruct") s.WriteObjectStart() s.WriteObjectField("Name") s.WriteString("xxx") s.WriteObjectEnd() s.WriteMore() s.WriteObjectField("Id") s.WriteInt(100) s.WriteObjectEnd() fmt.Println(string(s.Buffer())) // 输出:{"EmbedStruct":{"Name":"xxx"},"Id":100}s := jsoniter.ConfigDefault.BorrowStream(nil) // 记得把从Config中borrow过来的Stream实例Return回去 defer jsoniter.ConfigDefault.ReturnStream(s) s.WriteObjectStart() s.WriteObjectField("EmbedStruct") s.WriteObjectStart() s.WriteObjectField("Name") s.WriteString("xxx") s.WriteObjectEnd() s.WriteMore() s.WriteObjectField("Id") s.WriteInt(100) s.WriteObjectEnd() fmt.Println(string(s.Buffer())) // 输出:{"EmbedStruct":{"Name":"xxx"},"Id":100}
不过一般情况下,我们不会也不需要这么做,更多的时候是创建自己的Extension
或ValEncoder
时调用Stream
的这些方法来定制编码输出
6.4、另一种序列化接口
Stream
也提供了一个接口,可以实现跟Encoder
的Encode
方法基本一样的序列化功能
type testStructForStream struct{Field int}s := jsoniter.NewStream(jsoniter.ConfigDefault, nil, 1024)s.WriteVal(testStructForStream{300})result := s.Buffer()buf := make([]byte, len(result))copy(buf, result)fmt.Println(string(buf))// 输出:{"Field":300}type testStructForStream struct{ Field int } s := jsoniter.NewStream(jsoniter.ConfigDefault, nil, 1024) s.WriteVal(testStructForStream{300}) result := s.Buffer() buf := make([]byte, len(result)) copy(buf, result) fmt.Println(string(buf)) // 输出:{"Field":300}type testStructForStream struct{ Field int } s := jsoniter.NewStream(jsoniter.ConfigDefault, nil, 1024) s.WriteVal(testStructForStream{300}) result := s.Buffer() buf := make([]byte, len(result)) copy(buf, result) fmt.Println(string(buf)) // 输出:{"Field":300}
在上面这个例子里面,我们调用NewStream
来创建了一个Stream
实例,然后调用WriteVal
方法来实现序列化,最后将结果字节序列拷贝出来。通过这种方式,也可以完成序列化。实际上,json-iterator内部也是使用类似的方式,调用Stream
的WriteVal
来完成序列化。这里有两点需要说明:
- 调用
NewStream
创建Stream
实例,可以指定Stream
内部缓冲的大小。如果你的使用场景对性能有极致要求,而且序列化输出的json序列长度可以准确估计的话,不妨使用这个方法来取代直接调用Marshal
来进行序列化。通过指定内部缓冲的初始大小,避免后续在序列化过程中发生的扩容(Stream
内部存储序列化结果的缓存大小由其指定) - 上述例子中的拷贝操作是必须要进行的,不能直接调用
Stream
的Buffer
方法后返回的切片直接使用。因为Stream会复用其内部已经分配的缓冲,每次序列化都会把之前的内容复写掉,因此在下一次调用同一个Stream
实例进行序列化前,你必须把结果拷贝走
6.5、复用Stream实例
如果你需要复用同一个Stream
实例,记得在每次序列化完成后,重置你的Stream
type testStructForStream struct{Field int}s := jsoniter.ConfigDefault.BorrowStream(os.Stdout)defer jsoniter.ConfigDefault.ReturnStream(s)s.WriteVal(testStructForStream{300})result := s.Buffer()tmp := make([]byte, len(result))copy(tmp, result)// xxxxxx// ......if s.Error != nil{return}// 记得重置你的Streams.Reset(nil)s.WriteVal(testStructForStream{400})type testStructForStream struct{ Field int } s := jsoniter.ConfigDefault.BorrowStream(os.Stdout) defer jsoniter.ConfigDefault.ReturnStream(s) s.WriteVal(testStructForStream{300}) result := s.Buffer() tmp := make([]byte, len(result)) copy(tmp, result) // xxxxxx // ...... if s.Error != nil{ return } // 记得重置你的Stream s.Reset(nil) s.WriteVal(testStructForStream{400})type testStructForStream struct{ Field int } s := jsoniter.ConfigDefault.BorrowStream(os.Stdout) defer jsoniter.ConfigDefault.ReturnStream(s) s.WriteVal(testStructForStream{300}) result := s.Buffer() tmp := make([]byte, len(result)) copy(tmp, result) // xxxxxx // ...... if s.Error != nil{ return } // 记得重置你的Stream s.Reset(nil) s.WriteVal(testStructForStream{400})
请注意,如果你的Stream
在序列化过程中出现了错误,即Stream.Error
不为nil,那么你不能继续使用这个Stream
实例进行新的序列化或编码输出,即使你调了Reset
进行重置也不行,只能重新另外创建一个新的Stream
来使用(至少目前的实现必须这样)
6.6、Flush到输出设备
如果你在创建Stream
时,指定了使用的io.Writer
,并希望你序列化后的json写入到这里,而不是通过调用Buffer
来获取结果的话,记得在序列化结束的时候调用Flush
来将Stream
中的缓冲刷到你的io.Writer
中
type testStructForStream struct{Field int}s := jsoniter.NewStream(jsoniter.ConfigDefault, os.Stdout, 1024)s.WriteVal(testStructForStream{300})// 如果没有这个调用,你的序列化结果将不会输出到你指定的Writers.Flush()// 输出:{"Field":300}type testStructForStream struct{ Field int } s := jsoniter.NewStream(jsoniter.ConfigDefault, os.Stdout, 1024) s.WriteVal(testStructForStream{300}) // 如果没有这个调用,你的序列化结果将不会输出到你指定的Writer s.Flush() // 输出:{"Field":300}type testStructForStream struct{ Field int } s := jsoniter.NewStream(jsoniter.ConfigDefault, os.Stdout, 1024) s.WriteVal(testStructForStream{300}) // 如果没有这个调用,你的序列化结果将不会输出到你指定的Writer s.Flush() // 输出:{"Field":300}
参考教程:
Config_cn
https://zhuanlan.zhihu.com/p/105956945
https://juejin.cn/post/7027306493246439431
如有侵犯您的版权,请及时联系3500663466#qq.com(#换@),我们将第一时间删除本站数据。
暂无评论内容