-
Notifications
You must be signed in to change notification settings - Fork 1k
Extension_cn
English | 中文
Config
可以提供部分选项来控制序列化/反序列化的行为,但是不能提供更精细的编码或解析控制,无法应对复杂的需求。json-iterator考虑了这一点,提供了Extension
的机制,来满足复杂的序列化/反序列化场景。
在介绍Extension
的使用之前,需要先介绍一下ValEncoder
和ValDecoder
,因为Extension
的本质上就是针对不同的类型创建不同的ValEncoder
和ValDecoder
实现的。注意,ValEncoder
/ValDecoder
和json.Encoder
/json.Decoder
是不一样的概念,不要混淆了。
-
ValEncoder
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 } 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) }
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) }
Decode
函数中,调用iter
的接口从json输入源中读取了一个int64
的数值,接下来因为我们这个ValDecoder
对应的数据类型是time.Time
,这里把ptr
转换成指向time.Time
类型的指针,并以我们读入的int64
数值为unix时间初始化了一个time.Time
对象,最后将它赋给ptr
指向的数值。这样,我们就完成了从json串中读入unix时间,并将其转换成time.Time
对象的功能 -
要定制序列化/反序列化扩展,需要实现Extension
接口,并通过RegisterExtension
进行注册,Extension
包含以下方法:
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 }
上面的例子,首先我们用
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) 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 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再赋值
json-iterator有两个RegisterExtension
接口可以调用,一个是package级别的jsoniter.RegisterExtension
,一个是API
(说明见Config章节)级别的API.RegisterExtension
。这两个函数都可以用来注册扩展,但是两种注册方式注册的扩展的作用域略有不同。jsoniter.RegisterExtension
注册的扩展,对于所有Config
生成的API
都生效;而API.RegisterExtension
只对其对应的Config
生成的API
接口生效,这个需要注意