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`接口新建一个
```go
s := 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后,再写入到输出的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"}
这个例子里面,我们针对testStructForStream
的Field
字段注册了一个ValEncoder
,这个ValEncoder
调用Stream
的WriteString
方法,将ptr
指向的Int
类型数值以字符串的方式写入到json串Stream
开放了各种类型数据的写入方法,可以让我们很方便地去定制自己的数据以何种方式输出成json:
WriteBool
WriteInt
WriteFloat32
WriteString
WriteArrayStart
、WriteArrayEnd
WriteObjectStart
、WriteObjectEnd
WriteEmptyArray
WriteEmptyObject
......
手动构造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
}
// 记得重置你的Stream
s.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})
// 如果没有这个调用,你的序列化结果将不会输出到你指定的Writer
s.Flush()
// 输出:{"Field":300}