json-iterator中使用Stream来控制json的编码输出,通过其提供的API,配合自定义的Extension或ValEncoder,我们可以定制我们的数据如何编码输出成json,甚至可以从头构造并输出一个json串
创建Stream实例
有两种方法可以创建Stream实例:
- 从
API对象的Stream实例池中Borrow一个 ```go c := jsoniter.ConfigDefault s := c.BorrowStream(os.Stdout) defer c.ReturnStream(s)
// 你的功能实现 // xxxxxx // ……
使用这种方法"借用"的`Stream`实例,记得在使用完毕后"返还"回去2. 调用`NewStream`接口新建一个```gos := jsoniter.NewStream(jsoniter.ConfigDefault, os.Stdout, 1024)// 你的功能实现// xxxxxx// ......
使用这种方法,需要传入你的序列化配置对应生成的API对象,底层输出的io.Writer和指定Stream的内部缓冲内部大小(见下文详述)
定制编码输出
在定义你的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}
在上面的例子中,我们注册了一个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"}
这个例子里面,我们针对testStructForStream的Field字段注册了一个ValEncoder,这个ValEncoder调用Stream的WriteString方法,将ptr指向的Int类型数值以字符串的方式写入到json串Stream开放了各种类型数据的写入方法,可以让我们很方便地去定制自己的数据以何种方式输出成json:
WriteBoolWriteIntWriteFloat32WriteStringWriteArrayStart、WriteArrayEndWriteObjectStart、WriteObjectEndWriteEmptyArrayWriteEmptyObject......
手动构造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}
不过一般情况下,我们不会也不需要这么做,更多的时候是创建自己的Extension或ValEncoder时调用Stream的这些方法来定制编码输出
另一种序列化接口
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}
在上面这个例子里面,我们调用NewStream来创建了一个Stream实例,然后调用WriteVal方法来实现序列化,最后将结果字节序列拷贝出来。通过这种方式,也可以完成序列化。实际上,json-iterator内部也是使用类似的方式,调用Stream的WriteVal来完成序列化。这里有两点需要说明:
- 调用
NewStream创建Stream实例,可以指定Stream内部缓冲的大小。如果你的使用场景对性能有极致要求,而且序列化输出的json序列长度可以准确估计的话,不妨使用这个方法来取代直接调用Marshal来进行序列化。通过指定内部缓冲的初始大小,避免后续在序列化过程中发生的扩容(Stream内部存储序列化结果的缓存大小由其指定) - 上述例子中的拷贝操作是必须要进行的,不能直接调用
Stream的Buffer方法后返回的切片直接使用。因为Stream会复用其内部已经分配的缓冲,每次序列化都会把之前的内容复写掉,因此在下一次调用同一个Stream实例进行序列化前,你必须把结果拷贝走复用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})
请注意,如果你的Stream在序列化过程中出现了错误,即Stream.Error不为nil,那么你不能继续使用这个Stream实例进行新的序列化或编码输出,即使你调了Reset进行重置也不行,只能重新另外创建一个新的Stream来使用(至少目前的实现必须这样)
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}
