golang jsoniter extension 处理动态字段
source link: https://blog.csdn.net/oqqYuan1234567890/article/details/129970519
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
golang 原生 json 包,在处理 json 对象的字段的时候,是需要严格匹配类型的。但是,实际上,当我们与一些老系统或者脚本语言的系统对接的时候,有时候需要对类型需要做一下兼容,假设我们有以下需求
目标类型 | 输入 | 解析后 | |
---|---|---|---|
int | int, string | 123, “123” | 123 |
string | int, string | 123, “123” | “123” |
time | unix_seconds, RFC3339 | 1680676884, “2023-04-05T14:41:24Z”, | “2023-04-05T14:41:24Z” |
2. 可选项
我们以 time 作为一个样例
-
包装类,然后重新实现 Unmarshal 接口
type MyTime struct { t time.Time }
功能可以实现,但是如果使用的地方很多的情况下,就可能要
改动多处
,而且,这是全局级别
的,可能会影响到很多包的行为 -
使用 jsonter 的 extension 实现
jsoniter 的插件文档参考
我们使用实例级别的 extension, 而非全局,可以针对不同业务逻辑有所区分package main import ( "fmt" "reflect" "strconv" "time" "unsafe" jsoniter "github.com/json-iterator/go" "github.com/modern-go/reflect2" ) type sampleExtension struct { jsoniter.DummyExtension } type wrapEncoder struct { encodeFunc func(ptr unsafe.Pointer, stream *jsoniter.Stream) isEmptyFunc func(ptr unsafe.Pointer) bool decodeFunc func(ptr unsafe.Pointer, iter *jsoniter.Iterator) } func (enc *wrapEncoder) Encode(ptr unsafe.Pointer, stream *jsoniter.Stream) { enc.encodeFunc(ptr, stream) } func (codec *wrapEncoder) Decode(ptr unsafe.Pointer, iter *jsoniter.Iterator) { codec.decodeFunc(ptr, iter) } func (enc *wrapEncoder) IsEmpty(ptr unsafe.Pointer) bool { if enc.isEmptyFunc == nil { return false } return enc.isEmptyFunc(ptr) } // 这里统一改用 unix seconds 进行输出 func (e *sampleExtension) CreateEncoder(typ reflect2.Type) jsoniter.ValEncoder { if typ.Kind() == reflect.Struct && typ.Type1().PkgPath() == "time" && typ.String() == "time.Time" { return &wrapEncoder{ func(ptr unsafe.Pointer, stream *jsoniter.Stream) { t := *(*time.Time)(ptr) data := strconv.Itoa(int(t.Unix())) stream.WriteRaw(data) }, nil, nil, } } return nil } func (e *sampleExtension) CreateDecoder(typ reflect2.Type) jsoniter.ValDecoder { if typ.Kind() == reflect.Struct && typ.Type1().PkgPath() == "time" && typ.String() == "time.Time" { return &wrapEncoder{ decodeFunc: func(ptr unsafe.Pointer, iter *jsoniter.Iterator) { switch iter.WhatIsNext() { case jsoniter.NumberValue: // 兼容 unix 数字解析 timeUnix := iter.ReadInt() newTime := time.Unix(int64(timeUnix), 0) *(*time.Time)(ptr) = newTime case jsoniter.NilValue: iter.Skip() case jsoniter.StringValue: timeStr := iter.ReadString() newTime, err := time.Parse(time.RFC3339, timeStr) if err != nil { fmt.Println("Unmarshal err", err) } *(*time.Time)(ptr) = newTime } }, } } return nil } type Person struct { Birth time.Time `json:"birth"` } func main() { extension := &sampleExtension{} jsoniterAPI := jsoniter.Config{}.Froze() jsoniterAPI.RegisterExtension(extension) var p1 = Person{ Birth: time.Now(), } j, err := jsoniterAPI.MarshalToString(p1) if err != nil { panic(err) } fmt.Println(j) var p2 Person err = jsoniterAPI.Unmarshal([]byte(`{"birth": 1680254527}`), &p2) if err != nil { panic(err) } fmt.Println("p2", p2) var p3 Person err = jsoniterAPI.Unmarshal([]byte(`{"birth": "2023-03-21T07:20:04+00:00"}`), &p3) if err != nil { panic(err) } fmt.Println("p3", p3) var p4 Person err = jsoniterAPI.Unmarshal([]byte(`{"birth": null}`), &p4) if err != nil { panic(err) } fmt.Println("p4", p4) }
我们在例子中,实现了:
- 把 p1 使用了 unix 数字进行序列化
- 在反序列化 p2/p3/p4的时候,兼容了
字符串/数字/null
jsoniter 包提供了比较完善的定制能力,通过例子可以感受一下扩展性。后续大家可以根据业务需求发掘更多的能力
Recommend
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK