日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

如何在Flutter上优雅地序列化一个对象

發布時間:2024/8/23 编程问答 29 豆豆
生活随笔 收集整理的這篇文章主要介紹了 如何在Flutter上优雅地序列化一个对象 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

序列化一個對象才是正經事

對象的序列化反序列化是我們日常編碼中一個非常基礎的需求,尤其是對一個對象的json encode/decode操作。每一個平臺都會有相關的庫來幫助開發者方便得進行這兩個操作,比如Java平臺上赫赫有名的GSON,阿里巴巴開源的fastJson等等。

而在flutter上,借助官方提供的JsonCodec,只能對primitive/Map/List這三種類型進行json的encode/decode操作,對于復雜類型,JsonCodec提供了receiver/toEncodable兩個函數讓使用者手動“打包”和“解包”。

顯然,JsonCodec提供的功能看起來相當的原始,在閑魚app中存在著大量復雜對象序列化需求,如果使用這個類,就會出現集體“帶薪序列化”的盛況,而且還無法保證正確性。

來自官方推薦

聰明如Google官方,當然不會坐視不理。json_serializable的出現就是官方給出的推薦,它借助Dart Build System中的build_runner和json_annotation庫,來自動生成fromJson/toJson函數內容。(關于使用build_runner生成代碼的原理,之前興往同學的文章已經有所提及)

關于如何使用json_serializable網上已經有很多文章了,這里只簡單提一些步驟:

  • Step 1 創建一個實體類
  • Step 2 生成代碼:

來讓build runner生成序列化代碼。運行完成后文件夾下會出現一個xxx.g.dart文件,這個文件就是生成后的文件。

  • Step 3 代理實現:

把fromJson和toJson操作代理給上面生成出來的類

我們為什么不用這個實現

json_serializable完美實現了需求,但它也有不滿足需求的一面:

  • 使用起來有些繁瑣,多引入了一個類
  • 很重要的一點是,大量的使用"as"會給性能和最終產物大小產生不小的影響。實際上閑魚內部的《flutter編碼規范》中,是不建議使用"as"的。(對包大小的影響可以參見三笠同學的文章,同時dart linter也對as的性能影響有所描述)

一種正經的方式

基于上面的分析,很明顯的,需要一種新的方式來解決我們面臨的問題,我們暫且叫它,fish-serializable

需要實現的功能

我們首先來梳理一下,一個序列化庫需要用到:

  • 獲取可序列化對象的所有field以及它們的類型信息
  • 能夠構造出一個可序列化對象,并對它里面的fields賦值,且類型正確
  • 支持自定義類型
  • 最好能夠解決泛型的問題,這會讓使用更加方便
  • 最好能夠輕松得在不同的序列化/反序列化方式中切換,例如json和protobuf。
  • 困難在哪里

  • flutter禁用了dart:mirrors,反射API無法使用,也就無法通過反射的方式new一個instance、掃描class的fields。
  • 泛型的問題由于dart不進行類型擦出,可以獲取,但泛型嵌套后依然無法解開。
  • Let's rock

    無法使用dart:mirrors是個“硬”問題,沒有反射的支持,類的內容就是一個黑盒。于是我們在邁出第一步的時候就卡殼了- -!

    這個時候筆者腦子里閃過了很多畫面,白駒過隙,烏飛兔走,啊,不是...是c++,c++作為一種無法使用反射的語言,它是如何實現對象的 序列化/反序列化 操作的呢?

    一頓搜索猛如虎之后,發現大神們使用創建類對象的回調函數配合宏的方式來實現c++中類似反射這樣的操作。

    這個時候,筆者又想到了曾經朝夕相處的Android(現在已經變成了flutter),Android中的Parcelable序列化協議就是一個很好的參照,它通過writeXXXAPIs將類的數據寫入一個中間存儲進行序列化,再通過readXXXAPIs進行反序列化,這就解決了我們上面提到的第一個問題,既如何將一個類的“黑盒子”打開。

    同時,Parcelable協議中還需要使用者提供一個叫做CREATOR的靜態內部類,用來在反序列化的時候反射創建一個該類的對象或對象數組,對于沒有反射可用的我們來說,用c++的那種回調函數的方式就可以完美解決反序列化中對象創建的問題。

    于是最終我們的基本設計就是:

    • ValueHolder
    這是一個數據中轉存儲的基類,它內部的writeXXX APIs提供展開類內部的fields的能力,而readXXX則 用來將ValueHolder中的內容讀取賦值給類的fields。readList/readMap/readSerializable函數中的type argument,我們把它作為外部想要解釋數據的 方式,比如readSerializable<T>(key: 'object'),表示外部想要把key為object的值解釋為T類 型。
    • FishSerializable
    FishSerializable是一個interface,creator是個一個get函數,用來返回一個“創建類對象的回調”, writeTo函數則用來在反序列化的時候放置ValueHoder->fields的代碼。
    • JsonSerializer
    它繼承于FishSerializer接口,實現了encode/decode函數,并額外提供encodeToMap和 decodeFromMap功能。JsonSerializer類似JsonCodec,直接面向使用者用來json encode/decode

    以上,我們已經基本做好了一個flutter上支持對象序列化/反序列化操作的庫的基本架構設計,對象的序列化過程可以簡化為:

    由于ValueHolder中間存儲的存在,我們可以很方便得切換 序列化/反序列器,比如現有的JsonSerializer用來實現json的encode/decode,如果有類似protobuf的需求,我們則可以使用ProtoBufSerializer來將ValueHolder中的內容轉換成我們需要的格式。

    困難是不存在的

    有了基本的結構設計之后,實現的過程并非一帆風順。

    如何匹配類型?

    為了能支持泛型容器的解析,我們需要類似下面這樣的邏輯:

    List<SerializableObject> list = holder.readList<SerializableObject>(key: 'list');List<E> readList<E>({String key}){List<dynamic> list = _read(key); }E _flattenList<E>(List<dynamic> list){list?.map<E>((dynamic item){// 比較E是否屬于某個類型,然后進行對應類型的轉換 }); }

    在Java中,可以使用Class#isAssignableFrom,而在flutter中,我們沒有發現類似功能的API提供。而且,如果做下面這個測試,你還會發現一些很有意思的細節:

    void main() {print('int test');test<int>(1);print('\r\nint list test');test<List<int>>(<int>[]);print('\r\nobject test');test<A<int>>(A<int>()); }void test<T>(T t){print(T);print(t.runtimeType);print(T == t.runtimeType);print(identical(T, t.runtimeType)); }class A<T>{}

    輸出的結果是:

    可以看到,對于List這樣的容器類型,函數的type argument與instance的runtimeType無法比較,當然如果使用t is T,是可以返回正確的值的,但需要構造大量的對象。所以基本上,我們無法進行類型匹配然后做類型轉換。

    如何解析泛型嵌套?

    接下去就是如何分解泛型容器嵌套的問題,考慮如下場景:

    Map<String, List<int>> listMap;listMap = holder.readMap<String, List<int>>(key: 'listMap');

    readMap中得到的value type是一個List<int>,而我們沒有API去切割這個type argument。
    所以我們采用了一種比較“笨”也相對實用的方式。我們使用字符串切割了type argument,比如:

    List<int> => <String>[List<int>, List, int]

    然后在內部展開List或Map的時候,使用字符串匹配的方式匹配類型,在目前的使用中,完美得支持了標準List和Map容器互相嵌套。但目前無法支持標準List和Map之外的其他容器類型。

    What's more

    IDE插件輔助

    寫過Android的Parcelable的同學應該有種很深刻的體會,Parcelable協議中有大量的“機械”代碼需要寫,類似設計的fish-serializable也一樣。

    為了不被老板和使用庫的同學打死,同時開發了fish-serializable-intelij-plugin來自動生成這些“機械”代碼。

    與json_serializable的對比

    • fish-serializable在使用上配合IDE插件,減少了大量的"as"操作符的使用,同時在步驟上也更加簡短方便。
    • 相比于json_annotation生成的代碼,fish-serializable生成的代碼也更具可讀性,方便手動修改一些代碼實現。
    • fish-serializable可以通過手動接管 序列化/反序列化 過程的方式完美兼容json_annotation等其他方案。

    目前閑魚app中已經開始大量使用。

    開源計劃

    fish-serializable和fish-serializable-intelij-plugin都在開源計劃中,相信不久就可以與大家見面,盡請期待~

    ?


    原文鏈接
    本文為云棲社區原創內容,未經允許不得轉載。

    總結

    以上是生活随笔為你收集整理的如何在Flutter上优雅地序列化一个对象的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。

    主站蜘蛛池模板: 伊人超碰| 中文国产字幕 | 五月激情婷婷网 | 国产黄色大片免费看 | 国产精品无码久久久久 | 91亚洲天堂| 男女www | 亚洲+小说+欧美+激情+另类 | 久久国产视频精品 | 欧美精品一二三 | 热久久网站 | 欧美伦理在线观看 | 91久久精品一区二区三 | 中国xxxx性xxxx产国 | 天天噜天天干 | 又黄又色的网站 | 免费在线不卡av | 国产一区二区免费在线 | 欧美黑人添添高潮a片www | 日日夜夜国产 | 欧洲亚洲另类 | 欧美春色 | 亚洲天堂2020 | 天天操夜夜操夜夜操 | 999精品国产| 极品少妇在线 | 国产中文字幕在线播放 | 红色假期黑色婚礼2 | 豆花免费跳转入口官网 | 在线观看亚洲色图 | 在线观看免费观看在线 | 国产精品水嫩水嫩 | 老司机深夜福利视频 | 制服丝袜先锋影音 | 最新中文字幕第一页 | 亚洲欧美自偷自拍 | 日韩激情小视频 | 黑人玩弄人妻一区二区三区免费看 | 他揉捏她两乳不停呻吟动态图 | 色综合中文| 91午夜交换视频 | 国产深夜视频 | 久久久久一级 | 激情视频91| 97日日夜夜 | 美女扒开尿口让男人桶 | 男女插鸡视频 | 国产精品久久久久毛片大屁完整版 | 九九看片| 永久免费视频网站 | 日本免费色视频 | 中文字幕乱码一区 | 中文字幕麻豆 | 国产三级网 | 波多野结衣视频免费看 | av拍拍拍| 波多野结衣办公室33分钟 | 亚洲熟女www一区二区三区 | 久久一二三区 | 少妇伦子伦精品无吗 | 鲁鲁久久 | 成人福利网站在线观看 | 自拍偷拍亚洲天堂 | 亚洲av人人夜夜澡人人 | 深爱开心激情 | 精品久久久中文字幕 | 亚洲制服无码 | 性生活视频在线播放 | 国产香蕉精品视频 | 激情爱爱网站 | 成年女人18级毛片毛片免费 | 亚洲一区二区三区色 | 玖玖色在线 | 好屌妞视频这里有精品 | 欧美一级大片免费看 | 午夜精品久久久久久久爽 | www.自拍 | 青草一区二区 | 性欧美大战久久久久久久免费观看 | av资源网在线观看 | 色男天堂| japan高清日本乱xxxxx | 亚洲黄页网站 | 亚洲精品高潮久久久久久久 | 免费www xxx | 激情久久视频 | 成人做爰www免费看视频网站 | 五月综合在线 | 视频毛片 | 98堂 最新网名 | 日本少妇激情视频 | 婷婷久久综合网 | 手机在线成人av | 一级全黄男女免费大片 | 美日韩av| 艳妇臀荡乳欲伦交换在线播放 | 亚洲片国产一区一级在线观看 | 伊人国产视频 | 激情涩涩 |