- 
  Notifications
 You must be signed in to change notification settings 
- Fork 1.1k
Iterator_cn
English | 中文
json-iterator中使用Iterator来实现流式解析。通过其提供的API,我们可以控制json串的解析行为,我们可以对json串中与schema定义不一致的字段做兼容性的解析处理,也可以跳过我们不关心的json串中的片段
有三种方法可以创建Iterator实例:
- 
从 API对象的Iterator实例池中Borrow一个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 // ...... 使用这种方法,需要传入你的序列化配置对应生成的 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 // ...... 使用 Parse族的方法,可以在创建Iterator的时候指定待解析json串的输入源。其中Parse方法还可以指定Iterator用于解析的内部缓冲的大小
想象一个这样的场景:我们的数据结构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)) = 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
使用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]
在上面的例子中,我们调用了ParseString来创建一个Iterator实例。ParseString可以指定Iterator实例对应的配置和作为解析源的json串。然后我们调用了Iterator的ReadObjectCB方法,调用时必须传入一个回调函数。ReadObjectCB方法会解析一个对象类型的json串,并迭代这个json串中的顶层对象的每个字段,对每个字段都会调用我们一开始传进去的回调函数。这里可以看到,在回调函数里面,我们只是将传进来的字段名记录下来,然后调用Iterator的Skip来跳过这个字段对应的实际内容。Skip会自动解析json串中接下来的元素是什么类型的,然后跳过它的解析,跳到下一个字段。当遍历完毕后我们就可以拿到我们需要的字段列表了。
Iterator也提供了一个接口,可以实现跟Decoder的Decode方法基本一样的序列化功能
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的缓冲设置得大一点,提高解析效率
你可以调用Reset(解析源为io.Reader)或者ResetBytes(解析源为字符串或字节序列)来复用你的Iterator实例
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来使用(至少目前的实现必须这样)