Flutter JSON 解析最佳實踐

語言: CN / TW / HK

這篇文章其實早該寫了,之前的業餘時間一直花在開源專案或其它文章上了。

JSON 解析對於 Flutter 新人來講是個繞不開的話題,大家都在吐槽 Flutter 沒有反射,導致 JSON 解析無法像 Android 那樣方便,其實是不必要的,因為可以做到一樣方便。

網上講 JSON 解析的文章很多,大家自行去學習即可,本篇文章直接給出我創造出的、我認為的最佳方案,如有雷同,純屬巧合:

使用 JsonToDart 外掛自動生成 Bean 類,再使用 dynamic 關鍵字的能力,自動將 JSON 字串代表的資料填充到 Bean 類中

我們以如下 JSON 文字為例:

json { "nickName": "hackware", "realName": "陳方兵", "age": 29, "sex": "男" }

這是個 Person 物件的描述,我們先使用 JsonToDart 外掛將其轉換成 Bean 類,這樣我們就無需手寫解析程式碼了:

Snipaste_2022-07-02_07-31-58.png

Snipaste_2022-07-02_07-33-45.png

生成的 Bean 類程式碼如下:

```dart class Person { Person({ this.nickName, this.realName, this.age, this.sex, });

Person.fromJson(dynamic json) { nickName = json['nickName']; realName = json['realName']; age = json['age']; sex = json['sex']; }

String? nickName; String? realName; int? age; String? sex;

Map toJson() { final map = {}; map['nickName'] = nickName; map['realName'] = realName; map['age'] = age; map['sex'] = sex; return map; } } ```

這段程式碼最核心的是 fromJson 這個建構函式,由於 Flutter 中沒有反射,我們無法動態的呼叫 fromJson 方法。但我們可以先構造一個空的 Person 物件,再使用 dynamic 關鍵字呼叫它,但需要對 fromJson 做一下更改,將它從建構函式改為普通函式,如下:

dart Person fromJson(dynamic json) { nickName = json['nickName']; realName = json['realName']; age = json['age']; sex = json['sex']; return this; }

或是:

dart void fromJson(dynamic json) { nickName = json['nickName']; realName = json['realName']; age = json['age']; sex = json['sex']; }

真正的解析程式碼如下:

dart String jsonData = ''; // 從網路載入的 JSON 文字 Person person = Person().fromJson(jsonDecode(jsonData));

我想說明的是:使用反射自動建立物件和手動建立物件後再自動為該物件填充資料是一樣方便的。因此即便沒有反射,我們也能對網路請求做很好的封裝。

目前由於我用的是自創的 PVState 架構模式,它是 MVC 的改進版,它也是個輕量級的狀態管理方案。只有不到 120 行程式碼。它分為 PState 和 VState,這裡的 State 指的是 StatefulWidget 的 State。前者封裝業務邏輯,後者描述 UI,UI 和業務邏輯可以完全隔離。我把網路請求的基礎能力封裝到了 BasePState 中,如下:

dart void sendRequest<BEAN>({ required Future<Response<String>> call, required BEAN bean, OnStartCallback? startCallback, OnSuccessCallback<BEAN>? successCallback, OnFailCallback<BEAN>? failCallback, }) async { startCallback?.call(); bool? success; Object? exception; try { Response<String> resp = await call; dynamic result = (bean as dynamic).fromJson(jsonDecode(resp.data!)); success = result.success; } catch (e) { debugPrint('$e'); exception = e; } finally { try { if (success == true) { successCallback?.call(bean); } else { failCallback?.call(bean, exception); } } catch (e) { exception = e; failCallback?.call(bean, exception); } } }

真正發起請求的程式碼如下:

dart sendRequest( call: dio.get( 'https://xxx', ), bean: RealtimeAlarmListBean(), startCallback: () { setState(() { loadingRealtimeAlarm = true; }); }, successCallback: (RealtimeAlarmListBean bean) { setState(() { realtimeAlarmListBean = bean; loadingRealtimeAlarm = false; }); }, failCallback: (_, __) { setState(() { loadingRealtimeAlarm = false; showToast('請求失敗'); }); }, );

可見我在外部構造好了空的 Bean 物件傳進去,當請求回來後會把資料填充進去,最後在 successCallback 再把非空的 Bean 物件回傳回來。整個過程我沒有手動對 JSON 做解析。是不是挺方便的呢?

我比較喜歡這種網路模組的封裝模式,當然你也可以使用 async、await 做“同步”的封裝,蘿蔔青菜各有所愛吧。

這是目前我看到的最好的 JSON 解析方法,如果你有更好的方法,歡迎在評論區交流哦!我是中國第一位 Android & Flutter 雙料 GDFE,關注我的公眾號:FlutterFirst,帶你起飛!我們下期見。