ToB企服应用市场:ToB评测及商务社交产业平台

标题: github.com/json-iterator/go 详细教程 [打印本页]

作者: 美丽的神话    时间: 2023-9-1 19:44
标题: github.com/json-iterator/go 详细教程
最近接触到了 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

1.2、替代encoding/json

encoding/json可以很方便地迁移到json-iterator,并且迁移前后代码行为保持一致。不管你是使用基本的Marshal/Unmarshal接口,或是使用Encoder/Decoder,或是你已有的Marshaler/Unmarshaler实现,都能正常地工作。
这一点还是挺重要的,尤其是对于替换成该 json 库的项目。
  1. // import "encoding/json"
  2. //
  3. // json.Marshal(data)
  4. import "github.com/json-iterator/go"
  5. jsoniter.Marshal(data)
复制代码
只需要把import的package替换成"github.com/json-iterator/go",包名从"json",替换成"jsoniter"即可
1.3、序列化/反序列化配置

json-iterator提供了几种序列化/反序列化配置,供不同的场景下的使用
  1. api := jsoniter.Config{SortMapKeys:true}.Froze()
  2. b, err := api.Marshal(map[string]string{"C":"c", "A":"a", "B":"b"})
复制代码
上面的例子中,我们开启了SortMapKeys配置选项,让map序列化输出时字段进行排序输出。更多的选项说明,请参考Config章节
1.4、控制编解码行为

json-iterator提供了Extension机制,我们可以通过注册自己的Extension,来更精确地控制我们的序列化/反序列化行为
  1. type sampleExtension struct {
  2.     jsoniter.DummyExtension
  3. }
  4. func (e *sampleExtension) UpdateStructDescriptor(structDescriptor *jsoniter.StructDescriptor) {
  5.     if structDescriptor.Type.String() != "main.testStruct" {
  6.         return
  7.     }
  8.     binding := structDescriptor.GetField("TestField")
  9.     binding.FromNames = []string{"TestField", "Test_Field", "Test-Field"}
  10. }
  11. type testStruct struct {
  12.     TestField string
  13. }
  14. var t testStruct
  15. jsoniter.RegisterExtension(&sampleExtension{})
  16. err := jsoniter.Unmarshal([]byte(`{"TestField":"aaa"}`), &t)
  17. err = jsoniter.Unmarshal([]byte(`{"Test_Field":"bbb"}`), &t)
  18. err = jsoniter.Unmarshal([]byte(`{"Test-Field":"ccc"}`), &t)
复制代码
上面的例子中我们注册了一个Extension,它指定了testStruct的TestField字段名绑定到哪些字符串上,所有绑定的字符串在解析时都当成是该字段。更多的说明请参考Extension章节
1.5、快速操作json对象

json-iterator提供了Any对象,可以让你schemaless地从复杂嵌套json串中提取出感兴趣的部分
  1. jsoniter.Get([]byte(`{"Field":{"InnerField":{"Name":"Allen"}}}`), "Field", "InnerField", "Name").ToString()
  2. // output: Allen
复制代码
这里Get返回的是一个Any对象,我们获取嵌套结构体最内层的Name字段的Any对象,并调用ToString转换得到我们要的"Allen"字符串。更多的说明请参考Any章节
2、Config (重点)

下来内容全部来自官网,Config_cn 点击上面链接即可跳转。
json-iterator提供了一些常用的序列化/反序列化选型供配置,使用者可以根据自己需求打开/关闭特定的选项
2.1、配置选项

要配置序列化/反序列化选项,你需要创建一个Config结构体,并通过设置其字段来设置不同的选项,最后你还需要调用其Froze方法来生成这个Config对应的API对象,通过这个API对象来调用你的配置选项对应的序列化/反序列化函数。
  1. api := jsoniter.Config{SortMapKeys:true}.Froze()
  2. api.Marshal(data)
复制代码
这里我们创建了一个开启了SortMapKeys选项的Config,并生成其对应的API对象,来进行序列化
2.2、内置配置

json-iterator提供了三个内置的配置:
2.3、选项说明

我们知道,当我们为为某个类型实现了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是不一样的概念,不要混淆了。
3.2、定制你的扩展

要定制序列化/反序列化扩展,需要实现Extension接口,并通过RegisterExtension进行注册,Extension包含以下方法:
  1. type Extension interface {
  2.     UpdateStructDescriptor(structDescriptor *StructDescriptor)
  3.     CreateMapKeyDecoder(typ reflect2.Type) ValDecoder
  4.     CreateMapKeyEncoder(typ reflect2.Type) ValEncoder
  5.     CreateDecoder(typ reflect2.Type) ValDecoder
  6.     CreateEncoder(typ reflect2.Type) ValEncoder
  7.     DecorateDecoder(typ reflect2.Type, decoder ValDecoder) ValDecoder
  8.     DecorateEncoder(typ reflect2.Type, encoder ValEncoder) ValEncoder
  9. }
复制代码
当然,很多情况下,我们只需要用到里面的部分功能。json-iterator里面提供了一个DummyExtension,它是一个最基础的Extension实现(基本什么都不做或返回空)。当你在定义自己的Extension时,你可以匿名地嵌入DummyExtension,这样你就不需要实现所有的Extension成员,只需要关注自己需要的功能。下面我们通过一些例子,来说明Extension的各个成员函数可以用来做什么
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
  1. jsonStr := []byte(`{
  2.   "users": [
  3.     {
  4.       "username": "system",
  5.       "avatar_template": "/user_avatar/discourse.metabase.com/system/{size}/6_1.png",
  6.       "id": -1
  7.     },
  8.     {
  9.       "username": "zergot",
  10.       "avatar_template": "https://avatars.discourse.org/v2/letter/z/0ea827/{size}.png",
  11.       "id": 89
  12.     }
  13.   ],
  14.   "topics": {
  15.     "can_create_topic": false,
  16.     "more_topics_url": "/c/uncategorized/l/latest?page=1",
  17.     "draft": null,
  18.     "draft_key": "new_topic",
  19.     "draft_sequence": null,
  20.     "per_page": 30,
  21.     "topics": [
  22.       {
  23.         "bumped": true,
  24.         "id": 8,
  25.         "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.",
  26.         "category_id": 1,
  27.         "unseen": false,
  28.         "slug": "welcome-to-metabases-discussion-forum",
  29.         "fancy_title": "Welcome to Metabase\u0026rsquo;s Discussion Forum",
  30.         "bookmarked": null,
  31.         "archived": false,
  32.         "archetype": "regular",
  33.         "highest_post_number": 1,
  34.         "reply_count": 0,
  35.         "visible": true,
  36.         "closed": false,
  37.         "liked": null,
  38.         "posts_count": 1,
  39.         "views": 197,
  40.         "image_url": "/images/welcome/discourse-edit-post-animated.gif",
  41.         "created_at": "2015-10-17T00:14:49.526Z",
  42.         "last_posted_at": "2015-10-17T00:14:49.557Z",
  43.         "pinned": true,
  44.         "title": "Welcome to Metabase's Discussion Forum",
  45.         "has_summary": false,
  46.         "like_count": 0,
  47.         "pinned_globally": true,
  48.         "last_poster_username": "system",
  49.         "posters": [
  50.           {
  51.             "extras": "latest single",
  52.             "description": "Original Poster, Most Recent Poster",   // 我们需要这个
  53.             "user_id": -1
  54.           }
  55.         ],
  56.         "bumped_at": "2015-10-21T02:32:22.486Z",
  57.         "unpinned": null
  58.       }
  59.     ]
  60.   }
  61. }`)
复制代码
如果用传统的方法,那么首先我们应该先定义一个匹配这个json结构的结构体,然后调用Unmarshal来反序列化,再获取这个结构体中我们需要的字段的值。如果用Any,那么就很简单了:
  1. any := jsoniter.Get(jsonStr, "topics", "topics", 0, "posters", 0, "description")
  2. fmt.Println(any.ToString())
  3. // Output:
  4. // Original Poster, Most Recent Poster
复制代码
只需要一行,我们就可以拿到我们想要的元素。然后调用Any对象提供的接口做下转换,就得到了我们要的description字符串
4.2、与schema结合

还是上面的例子
  1. jsonStr := []byte(`{
  2.   "users": [
  3.     {
  4.       "username": "system",
  5.       "avatar_template": "/user_avatar/discourse.metabase.com/system/{size}/6_1.png",
  6.       "id": -1
  7.     },
  8.     {
  9.       "username": "zergot",
  10.       "avatar_template": "https://avatars.discourse.org/v2/letter/z/0ea827/{size}.png",
  11.       "id": 89
  12.     }
  13.   ],
  14.   "topics": {
  15.     "can_create_topic": false,
  16.     "more_topics_url": "/c/uncategorized/l/latest?page=1",
  17.     "draft": null,
  18.     "draft_key": "new_topic",
  19.     "draft_sequence": null,
  20.     "per_page": 30,
  21.     "topics": [
  22.       {
  23.         "bumped": true,
  24.         "id": 8,
  25.         "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.",
  26.         "category_id": 1,
  27.         "unseen": false,
  28.         "slug": "welcome-to-metabases-discussion-forum",
  29.         "fancy_title": "Welcome to Metabase\u0026rsquo;s Discussion Forum",
  30.         "bookmarked": null,
  31.         "archived": false,
  32.         "archetype": "regular",
  33.         "highest_post_number": 1,
  34.         "reply_count": 0,
  35.         "visible": true,
  36.         "closed": false,
  37.         "liked": null,
  38.         "posts_count": 1,
  39.         "views": 197,
  40.         "image_url": "/images/welcome/discourse-edit-post-animated.gif",
  41.         "created_at": "2015-10-17T00:14:49.526Z",
  42.         "last_posted_at": "2015-10-17T00:14:49.557Z",
  43.         "pinned": true,
  44.         "title": "Welcome to Metabase's Discussion Forum",
  45.         "has_summary": false,
  46.         "like_count": 0,
  47.         "pinned_globally": true,
  48.         "last_poster_username": "system",
  49.         "posters": [
  50.           {    // 这次我们需要这个
  51.             "extras": "latest single",
  52.             "description": "Original Poster, Most Recent Poster",   
  53.             "user_id": -1
  54.           }
  55.         ],
  56.         "bumped_at": "2015-10-21T02:32:22.486Z",
  57.         "unpinned": null
  58.       }
  59.     ]
  60.   }
  61. }`)
复制代码
这次我们需要"posters"数组的第一个结构体,我们现在已经有它的schema定义了,除此之外这个json的其他信息我都不需要,那么如何通过Any对象获得这个结构体呢?我们需要ToVal接口:
  1. type Poster struct {
  2.         Extras string `json:"extras"`
  3.         Desc string `json:"description"`
  4.         UserId int `json:"user_id"`
  5. }
  6. var p Poster
  7. any := jsoniter.Get(jsonStr, "topics", "topics", 0, "posters", 0)
  8. any.ToVal(&p)
  9. fmt.Printf("extras=%s\ndescription=%s\nuser_id=%d\n", p.Extras, p.Desc, p.UserId)
  10. // Output:
  11. // extras=latest single
  12. // description=Original Poster, Most Recent Poster
  13. // user_id=-1
复制代码
这里可以看到,首先我们拿到了"posters"第一个元素的Any对象,然后调用ToVal方法,就可以像之前的反序列化方法一样把数据解析出来。实际上,如果你的Any对象对应的是数组或对象类型的元素,它内部保存了这个元素原始的json串。当你需要获取其字段、元素或者将其反序列化出来的时候,才会触发解析。json-iterator内部将其称为懒解析。来看个数组的例子:
  1. type User struct {
  2.         UserName string `json:"username"`
  3.         Template string `json:"avatar_template"`
  4.         Id int `json:"id"`
  5. }
  6. var users []User
  7. any := jsoniter.Get(jsonStr, "users")
  8. fmt.Println(any.Get(0, "username").ToString())
  9. // Output:
  10. // system
  11. any.ToVal(&users)
  12. fmt.Printf("username=%s\navatar_template=%s\nid=%d\n", users[1].UserName, users[1].Template, users[1].Id)
  13. // Output:
  14. // username=zergot
  15. // avatar_template=https://avatars.discourse.org/v2/letter/z/0ea827/{size}.png
  16. // id=89
复制代码
数组元素的获取方法其实也是类似,这里不再详述。
有一点需要说明的是,只有数组和对象的json元素对应的Any才提供ToVal方法,也就是说这两种json元素的Any对象才实现了懒解析,其他诸如int,bool,string等都没有实现,实际上它们也不需要什么懒解析
5、Iterator

json-iterator中使用Iterator来实现流式解析。通过其提供的API,我们可以控制json串的解析行为,我们可以对json串中与schema定义不一致的字段做兼容性的解析处理,也可以跳过我们不关心的json串中的片段
5.1、创建Iterator实例

有三种方法可以创建Iterator实例:
5.2、定制解析行为

想象一个这样的场景:我们的数据结构schema中某个字段定义成了bool类型,但是我们接收到的json串中,该字段对应的值可能是bool类型,可能是int类型,还可能是string类型,我们需要对其做兼容性的解析处理,这时候Iterator(配合Extension或ValDecoder)就可以发挥作用了。
  1. type testStructForIterator struct{
  2.     BoolField bool
  3. }
  4. jsoniter.RegisterFieldDecoder(reflect2.TypeOf(testStructForIterator{}).String(), "BoolField",
  5.     &wrapDecoder{
  6.         func(ptr unsafe.Pointer, iter *jsoniter.Iterator) {
  7.             typ := iter.WhatIsNext()
  8.             switch typ {
  9.             case jsoniter.BoolValue:
  10.                 *((*bool)(ptr)) = iter.ReadBool()
  11.             case jsoniter.NumberValue:
  12.                 number := iter.ReadNumber()
  13.                 if n, err := number.Int64(); err == nil{
  14.                     if n > 0{
  15.                         *((*bool)(ptr)) = true
  16.                     }else{
  17.                         *((*bool)(ptr)) = false
  18.                     }
  19.                 }else{
  20.                     *((*bool)(ptr)) = false
  21.                 }
  22.             case jsoniter.StringValue:
  23.                 str := iter.ReadString()
  24.                 if str == "true"{
  25.                     *((*bool)(ptr)) = true
  26.                 }else{
  27.                     *((*bool)(ptr)) = false
  28.                 }
  29.             case jsoniter.NilValue:
  30.                 iter.ReadNil()
  31.                 *((*bool)(ptr)) = false
  32.             default:
  33.                 iter.ReportError("wrapDecoder", "unknown value type")
  34.             }
  35.         },
  36.     })
  37. t := testStructForIterator{}
  38. if err := jsoniter.Unmarshal([]byte(`{"BoolField":true}`), &t); err == nil{
  39.     fmt.Println(t.BoolField)
  40.     // 输出:true
  41. }
  42. if err := jsoniter.Unmarshal([]byte(`{"BoolField":1}`), &t); err == nil{
  43.     fmt.Println(t.BoolField)
  44.     // 输出:true
  45. }
  46. if err := jsoniter.Unmarshal([]byte(`{"BoolField":"true"}`), &t); err == nil{
  47.     fmt.Println(t.BoolField)
  48.     // 输出:true
  49. }
  50. if err := jsoniter.Unmarshal([]byte(`{"BoolField":"false"}`), &t); err == nil{
  51.     fmt.Println(t.BoolField)
  52.     // 输出:false
  53. }
  54. if err := jsoniter.Unmarshal([]byte(`{"BoolField":null}`), &t); err == nil{
  55.     fmt.Println(t.BoolField)
  56.     // 输出:false
  57. }
复制代码
在上面这个例子里面,我们针对testStructForIterator的BoolField字段注册了一个ValDecoder。在它的Decode方法中,我们先调用Iterator的WhatIsNext方法,通过json串中下一个元素的类似,来决定调用Iterator的哪个方法来解析下一个数值,根据解析结果,设置ptr指向的bool类型的数据值。这样不管我们解析的json串中,BoolField字段实际使用布尔、数值或是字符串来表示,我们都可以做到兼容
Iterator开放了各种接口用于从输入中读入不同类型的数据:
具体每个方法的说明可以参考godoc
5.3、跳过json片段

使用Iterator,我们可以跳过json串中的特定片段,只处理我们感兴趣的部分。考虑这么一个场景:我们接收到一个json串,这个json串中包含了一个对象,我们只想把这个对象的每个字段的字段名记录下来,至于字段对应的具体内容,我们不关心。为了实现这样的需求,我们需要用到Iterator
  1. jsonStr := `
  2. {
  3.         "_id": "58451574858913704731",
  4.         "about": "a4KzKZRVvqfBLdnpUWaD",
  5.         "address": "U2YC2AEVn8ab4InRwDmu",
  6.         "age": 27,
  7.         "balance": "I5cZ5vRPmVXW0lhhRzF4",
  8.         "company": "jwLot8sFN1hMdE4EVW7e",
  9.         "email": "30KqJ0oeYXLqhKMLDUg6",
  10.         "eyeColor": "RWXrMsO6xi9cpxPqzJA1",
  11.         "favoriteFruit": "iyOuAekbybTUeDJqkHNI",
  12.         "gender": "ytgB3Kzoejv1FGU6biXu",
  13.         "greeting": "7GXmN2vMLcS2uimxGQgC",
  14.         "guid": "bIqNIywgrzva4d5LfNlm",
  15.         "index": 169390966,
  16.         "isActive": true,
  17.         "latitude": 70.7333712683406,
  18.         "longitude": 16.25873969455544,
  19.         "name": "bvtukpT6dXtqfbObGyBU",
  20.         "phone": "UsxtI7sWGIEGvM2N1Mh0",
  21.         "picture": "8fiyZ2oKapWtH5kXyNDZJjvRS5PGzJGGxDCAk1he1wuhUjxfjtGIh6agQMbjovF10YlqOyzhQPCagBZpW41r6CdrghVfgtpDy7YH",
  22.         "registered": "gJDieuwVu9H7eYmYnZkz",
  23.         "tags": [
  24.                 "M2b9n0QrqC",
  25.                 "zl6iJcT68v",
  26.                 "VRuP4BRWjs",
  27.                 "ZY9jXIjTMR"
  28.         ]
  29. }
  30. `
  31. fieldList := make([]string, 0)
  32. iter := jsoniter.ParseString(jsoniter.ConfigDefault, jsonStr)
  33. iter.ReadObjectCB(func(iter *jsoniter.Iterator, field string) bool{
  34.     fieldList = append(fieldList, field)
  35.     iter.Skip()
  36.     return true
  37. })
  38. fmt.Println(fieldList)
  39. // 输出:[_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方法基本一样的序列化功能
  1. type testStructForIterator struct{
  2.     Name string
  3.     Id int
  4. }
  5. var dat testStructForIterator
  6. iter := jsoniter.Parse(jsoniter.ConfigDefault, nil, 1024)
  7. iter.ResetBytes([]byte(`{"Name":"Allen","Id":100}`))
  8. if iter.ReadVal(&dat); iter.Error == nil || iter.Error == io.EOF{
  9.     fmt.Println(dat)
  10.     // 输出:{Allen 100}
  11. }
复制代码
在上面这个例子里面,我们调用Parse来创建了一个Iterator实例,不设置输入设备io.Reader,我们用ResetBytes来设置待解析的json串,然后调用ReadVal方法来实现序列化。通过这种方式,也可以完成反序列化。实际上,json-iterator内部也是使用类似的方式,调用Iterator的ReadVal来完成反序列化。这里有一点需要说明:
5.5、复用Iterator实例

你可以调用Reset(解析源为io.Reader)或者ResetBytes(解析源为字符串或字节序列)来复用你的Iterator实例
  1. type testStructForIterator struct{
  2.     Name string
  3.     Id int
  4. }
  5. var dat testStructForIterator
  6. iter := jsoniter.ParseString(jsoniter.ConfigDefault, `{"Name":"Allen","Id":100}`)
  7. iter.ReadVal(&dat)
  8. // xxxxxx
  9. // ......
  10. if iter.Error != nil{
  11.     return
  12. }
  13. iter.ResetBytes([]byte(`{"Name":"Tom","Id":200}`))
  14. 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实例:
6.2、定制编码输出

在定义你的Extension或ValEncoder时,你需要用到Stream来定制你的字段如何输出成json
  1. type sampleExtension struct {
  2.     jsoniter.DummyExtension
  3. }
  4. type wrapEncoder struct {
  5.     encodeFunc  func(ptr unsafe.Pointer, stream *jsoniter.Stream)
  6.     isEmptyFunc func(ptr unsafe.Pointer) bool
  7. }
  8. func (enc *wrapEncoder) Encode(ptr unsafe.Pointer, stream *jsoniter.Stream) {
  9.     enc.encodeFunc(ptr, stream)
  10. }
  11. func (enc *wrapEncoder) IsEmpty(ptr unsafe.Pointer) bool {
  12.     if enc.isEmptyFunc == nil {
  13.         return false
  14.     }
  15.     return enc.isEmptyFunc(ptr)
  16. }
  17. func (e *sampleExtension) CreateEncoder(typ reflect2.Type) jsoniter.ValEncoder {
  18.     if typ.Kind() == reflect.Int {
  19.         return &wrapEncoder{
  20.             func(ptr unsafe.Pointer, stream *jsoniter.Stream) {
  21.                 // 将Int类型的变量的值+1000后,再写入到输出的json
  22.                 stream.WriteInt(*(*int)(ptr) + 1000)
  23.             },
  24.             nil,
  25.         }
  26.     }
  27.     return nil
  28. }
  29. func streamTest(){
  30.     jsoniter.RegisterExtension(&sampleExtension{})
  31.     j, _ := jsoniter.MarshalToString(1000)
  32.     fmt.Println(j)
  33.     // 输出:2000
  34. }
复制代码
在上面的例子中,我们注册了一个Extension,这个Extension的CreateEncoder函数中,我们调用了Stream的WriteInt接口,来将ptr指向的数值加1000后,再输出成json;在自定义ValEncoder中,我们同样使用Stream提供的函数来定制我们字段的输出
  1. type testStructForStream struct{
  2.     Field int
  3. }
  4. jsoniter.RegisterFieldEncoderFunc(reflect2.TypeOf(testStructForStream{}).String(), "Field",
  5. func(ptr unsafe.Pointer, stream *jsoniter.Stream) {
  6.     // 将Int类型的值转换成字符串类型的json输出
  7.     stream.WriteString(strconv.Itoa(*(*int)(ptr)))
  8. }, nil)
  9. j, _ := jsoniter.MarshalToString(testStructForStream{1024})
  10. fmt.Println(j)
  11. // 输出:{"Field":"1024"}
复制代码
这个例子里面,我们针对testStructForStream的Field字段注册了一个ValEncoder,这个ValEncoder调用Stream的WriteString方法,将ptr指向的Int类型数值以字符串的方式写入到json串
Stream开放了各种类型数据的写入方法,可以让我们很方便地去定制自己的数据以何种方式输出成json:
具体每个方法的说明可以参考godoc
6.3、手动构造json输出

使用Stream,可以完全手动地构造你的json如何输出成字节流
  1. s := jsoniter.ConfigDefault.BorrowStream(nil)
  2. // 记得把从Config中borrow过来的Stream实例Return回去
  3. defer jsoniter.ConfigDefault.ReturnStream(s)
  4. s.WriteObjectStart()
  5. s.WriteObjectField("EmbedStruct")
  6. s.WriteObjectStart()
  7. s.WriteObjectField("Name")
  8. s.WriteString("xxx")
  9. s.WriteObjectEnd()
  10. s.WriteMore()
  11. s.WriteObjectField("Id")
  12. s.WriteInt(100)
  13. s.WriteObjectEnd()
  14. fmt.Println(string(s.Buffer()))
  15. // 输出:{"EmbedStruct":{"Name":"xxx"},"Id":100}
复制代码
不过一般情况下,我们不会也不需要这么做,更多的时候是创建自己的Extension或ValEncoder时调用Stream的这些方法来定制编码输出
6.4、另一种序列化接口

Stream也提供了一个接口,可以实现跟Encoder的Encode方法基本一样的序列化功能
  1. type testStructForStream struct{
  2.     Field int
  3. }
  4. s := jsoniter.NewStream(jsoniter.ConfigDefault, nil, 1024)
  5. s.WriteVal(testStructForStream{300})
  6. result := s.Buffer()
  7. buf := make([]byte, len(result))
  8. copy(buf, result)
  9. fmt.Println(string(buf))
  10. // 输出:{"Field":300}
复制代码
在上面这个例子里面,我们调用NewStream来创建了一个Stream实例,然后调用WriteVal方法来实现序列化,最后将结果字节序列拷贝出来。通过这种方式,也可以完成序列化。实际上,json-iterator内部也是使用类似的方式,调用Stream的WriteVal来完成序列化。这里有两点需要说明:
6.5、复用Stream实例

如果你需要复用同一个Stream实例,记得在每次序列化完成后,重置你的Stream
  1. type testStructForStream struct{
  2.     Field int
  3. }
  4. s := jsoniter.ConfigDefault.BorrowStream(os.Stdout)
  5. defer jsoniter.ConfigDefault.ReturnStream(s)
  6. s.WriteVal(testStructForStream{300})
  7. result := s.Buffer()
  8. tmp := make([]byte, len(result))
  9. copy(tmp, result)
  10. // xxxxxx
  11. // ......
  12. if s.Error != nil{
  13.     return
  14. }
  15. // 记得重置你的Stream
  16. s.Reset(nil)
  17. s.WriteVal(testStructForStream{400})
复制代码
请注意,如果你的Stream在序列化过程中出现了错误,即Stream.Error不为nil,那么你不能继续使用这个Stream实例进行新的序列化或编码输出,即使你调了Reset进行重置也不行,只能重新另外创建一个新的Stream来使用(至少目前的实现必须这样)
6.6、Flush到输出设备

如果你在创建Stream时,指定了使用的io.Writer,并希望你序列化后的json写入到这里,而不是通过调用Buffer来获取结果的话,记得在序列化结束的时候调用Flush来将Stream中的缓冲刷到你的io.Writer中
  1. type testStructForStream struct{
  2.     Field int
  3. }
  4. s := jsoniter.NewStream(jsoniter.ConfigDefault, os.Stdout, 1024)
  5. s.WriteVal(testStructForStream{300})
  6. // 如果没有这个调用,你的序列化结果将不会输出到你指定的Writer
  7. s.Flush()
  8. // 输出:{"Field":300}
复制代码
参考教程:
Config_cn
https://zhuanlan.zhihu.com/p/105956945
https://juejin.cn/post/7027306493246439431

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!




欢迎光临 ToB企服应用市场:ToB评测及商务社交产业平台 (https://dis.qidao123.com/) Powered by Discuz! X3.4