Flutter Json自動反序列化——json_serializable v1.5.1 | 掘金技術徵文

前言

Google推出flutter這樣一個新的高性能跨平臺(Android,ios)快速開發框架以後,被業界許多開發者所關注。我在接觸了flutter以後發現這個確實是一個好東西,好東西固然要和你們分享,對吧。html

今天要跟你們分享的是Json反序列化的實現。相信作app的同窗都會遇到這麼一個問題,在向服務器請求數據後,服務器每每會返回一段json字符串。而咱們要想更加靈活的使用數據的話須要把json字符串轉化成對象。因爲flutter只提供了json to Map。而手寫反序列化在大型項目中極不穩定,很容易致使解析失敗。因此今天給你們介紹的是flutter團隊推薦使用的 json_serializable 自動反序列化。ios

你將學到什麼

  • flutter中如何解析json對象
  • 如何使用自動生成工具生成代碼
  • 如何測試你的數據

開始json反序列化

第一步:建立mock數據

在實際開發過程當中,咱們可能會對以前的一些代碼進行修改。當咱們代碼功能複雜而且量足夠大的時候,咱們須要使用單元測試來保證新添加的代碼不會影響以前所寫的代碼。而服務器的數據常常會變化,因此第一步固然是建立一個咱們的mock數據啦。git

這裏使用了GITHUB/HackerNews的數據(github.com/HackerNews/…)github

abstract class JsonString{
  static final String mockdata = ''' { "by" : "dhouston", "descendants" : 71, "id" : 8863, "kids" : [ 8952, 9224, 8917, 8884, 8887, 8943, 8869, 8958, 9005, 9671, 8940, 9067, 8908, 9055, 8865, 8881, 8872, 8873, 8955, 10403, 8903, 8928, 9125, 8998, 8901, 8902, 8907, 8894, 8878, 8870, 8980, 8934, 8876 ], "score" : 111, "time" : 1175714200, "title" : "My YC app: Dropbox - Throw away your USB drive", "type" : "story", "url" : "http://www.getdropbox.com/u/2/screencast.html" }''';
}
複製代碼

第二步:添加依賴

在pubspec.yaml中添加以下依賴json

dependencies:
  # Your other regular dependencies here
 json_annotation: ^1.2.0

dev_dependencies:
  # Your other dev_dependencies here
 build_runner: ^0.10.2
 json_serializable: ^1.5.1
複製代碼

這裏須要添加三個依賴,它們分別是:"json_annotation" "build_runner" 和 "json_serializable"。服務器

請注意,yaml配置文件對於縮進要求十分嚴格,下面的build_runner和json_serializable應該是與flutter_test平級的,千萬不要寫在flutter_test縮進後,這樣它會認爲這兩個是flutter_test的子集目錄!app

因爲不少朋友在這一步遇到了問題,這裏貼出源碼框架

第三步:根據json建立實體類

咱們這裏根據上面的json數據寫好了一個dart的實體類ide

class Data{
  final String by;
  final int descendants;
  final int id;
  final List<int> kids;
  final int score;
  final int time;
  final String title;
  final String type;
  final String url;

  Data({this.by, this.descendants, this.id, this.kids, this.score, this.time,
    this.title, this.type, this.url});
}
複製代碼

咱們在這裏使用了dart語法糖建立了構造函數。具體請參考(www.dartlang.org/guides/lang…)。函數

第四步:關聯實體類文件

咱們須要在咱們的實體類中關聯生成文件。

import 'package:json_annotation/json_annotation.dart';

part 'data.g.dart';

@JsonSerializable()
class Data {
  final String by;
  final int descendants;
  final int id;
  final List<int> kids;
  final int score;
  final int time;
  final String title;
  final String type;
  @JsonKey(nullable: false)
  final String url;

  Data({this.by, this.descendants, this.id, this.kids, this.score, this.time,
    this.title, this.type, this.url});
複製代碼

剛寫完data.g.dart的會報錯,這是正常的!由於咱們還沒生成解析文件

  • 爲了使實體類文件找到生成文件,咱們須要 part 'data.g.dart'。

第五步:生成Json解析文件

噹噹噹...!這裏開始就是重頭戲了!!

咱們要使用JsonSerializable生成代碼的話必需要在須要生成代碼的實體類前添加註解@JsonSerializable(),而要使用這個註解咱們必須引入json_annotation/json_annotation.dart這個包。

import 'package:json_annotation/json_annotation.dart';

@JsonSerializable()
class Data{
  final String by;
  final int descendants;
  final int id;
  final List<int> kids;
  final int score;
  final int time;
  final String title;
  final String type;
  final String url;

  Data({this.by, this.descendants, this.id, this.kids, this.score, this.time,
    this.title, this.type, this.url});
}
複製代碼

這裏須要注意,flutter編碼規範dart文件名統一小寫,這樣能夠避免不少問題。ok這樣實體類就建立完成啦。

那麼問題來了,應該如何生成代碼呢?

還記得咱們剛纔添加的build_runner的依賴嗎,這時候咱們就須要它來幫忙咯。

build_runner

build_runner是dart團隊提供的一個生成dart代碼文件的外部包。

咱們在當前項目的目錄下運行flutter packages pub run build_runner build

運行成功後咱們應該能在這個實體類的下面發現一個新的文件

這個data.g.dart就是build_runner根據JsonSerializable生成的json解析文件。 咱們來看看這個生成的dart文件

// GENERATED CODE - DO NOT MODIFY BY HAND

part of 'data.dart';

// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************

Data _$DataFromJson(Map<String, dynamic> json) {
  return Data(
      by: json['by'] as String,
      descendants: json['descendants'] as int,
      id: json['id'] as int,
      kids: (json['kids'] as List)?.map((e) => e as int)?.toList(),
      score: json['score'] as int,
      time: json['time'] as int,
      title: json['title'] as String,
      type: json['type'] as String,
      url: json['url'] as String);
}

Map<String, dynamic> _$DataToJson(Data instance) => <String, dynamic>{
      'by': instance.by,
      'descendants': instance.descendants,
      'id': instance.id,
      'kids': instance.kids,
      'score': instance.score,
      'time': instance.time,
      'title': instance.title,
      'type': instance.type,
      'url': instance.url
    };

複製代碼

同志們請注意這段代碼最上面的註釋"// GENERATED CODE - DO NOT MODIFY BY HAND"。你可千萬別手寫生成文件啊哈哈哈哈。

這段代碼生成實體類庫的一個part,在老版本part中有一個抽象實體類的mixin,dart中使用基於mixin的繼承來解決單繼承的侷限性。

新版本中聲稱了兩個方法,fromJson和toJson方法,它們能幹什麼相信你們從名字上就能猜到了。

【對part感興趣的同窗能夠參考https://juejin.im/post/5b601f40e51d4519575a5036#heading-8 Dart | 淺析dart中庫的導入與拆分。

對mixin感興趣的同窗能夠在(www.dartlang.org/guides/lang…)瞭解更多關於mixin的知識。】

  • _$DataFromJson:它接收了一個map:Map<String, dynamic>,並將這個Map裏的值映射爲咱們所須要的實體類對象。咱們就可使用這個方法,將存有json數據的map轉化爲咱們須要的實體類對象。
  • _$DataToJson:將調用此方法的對象直接根據字段映射成Map。

而這兩個都是私有方法,part讓兩個文件共享做用域與命名空間,因此咱們須要將生成的方法暴露給外部。

而後咱們再回到實體類中將 添加fromJson 和 toJson方法。

import 'package:json_annotation/json_annotation.dart';

@JsonSerializable()
class Data{
  final String by;
  final int descendants;
  final int id;
  final List<int> kids;
  final int score;
  final int time;
  final String title;
  final String type;
  final String url;

  Data({this.by, this.descendants, this.id, this.kids, this.score, this.time,
    this.title, this.type, this.url});
//反序列化
  factory Data.fromJson(Map<String, dynamic> json) => _$DataFromJson(json);
//序列化
  Map<String, dynamic> toJson() => _$DataToJson(this);
}
複製代碼
  • 提供一個工廠構造方法Data.fromJson,該方法實際調用生成文件的DataFromJson方法。
  • 提供一個toJson()序列化對象的方法,實際調用生成文件的_$DataToJson()方法,並將調用對象解析生成Map<String ,dynamic>。

這樣Json反序列化的工做就完成啦!

第六步:JSON反序列化

咱們剛纔實現了Map to Dart,但是咱們須要的是json to dart。這時候就須要dart自帶的 dart:convert 來幫助咱們了。

dart:convert

dart:convert是dart提供用於在不一樣數據表示之間進行轉換的編碼器和解碼器,可以解析JSON和UTF-8。

也就是說咱們須要先將json數據使用dart:convert轉成Map,咱們就能經過Map轉爲dart對象了。

使用方法

Map<String ,dynamic> map = json.decode("jsondata");
複製代碼

知道了如何將jsonString解析成map之後咱們就能直接將json轉化爲實體對象啦。

轉化方法

Data data = Data.fromJson(json.decode('jsondata'));
複製代碼

第七步:編寫單元測試

flutter給咱們提供了單元測試,它的好處在於,咱們想要驗證代碼的正確性不用跑整個程序,每次只用跑一個單元測試文件。並且養成習慣編寫單元測試之後,可以保證之後在開發過程當中快速精肯定位錯誤,避免新加入的代碼破壞老的代碼引發項目崩潰。每次應用啓動前都會跑一遍單元測試以確保項目可以正確運行。在實際開發中咱們應該使用的mock數據來做爲測試數據來源。

使用方法: 右鍵run這個測試文件

import 'dart:convert';

import 'package:flutter_test/flutter_test.dart';
import 'package:wmkids/data/mockdata.dart';
import 'package:wmkids/data/data.dart';

void main(){
  group('jsonparse test', (){
    test('mockdata test', (){
      Data data1 = Data.fromJson(json.decode(JsonString.mockdata));
      expect(data1.url, 'http://www.getdropbox.com/u/2/screencast.html');
    });
  });
}
複製代碼

咱們使用到了第一步建立的mock數據,並驗證了該json的url,假如咱們解析正確這個單元測試將會經過。

這裏的group是一組測試,一個group中能夠有多個test驗證咱們的代碼是否正確。

expect(data1,data2);會check咱們的data1與data2的值是否相等,假如同樣的話就會經過測試。假如不同的話會告訴咱們哪裏不同。

經常使用場景特殊處理辦法

對象嵌套場景下的json解析

在json中常常會使用嵌套信息,咱們在解析成dart文件的時候須要解析成對象嵌套。在這種場景下須要將編寫步驟作一個調整。 咱們須要在編寫實體類的時候就帶上工廠方法,由於對象存在依賴關係,先要保證子對象是serializable的才能保證父對象成功解析。

這裏提示有錯誤時正常的,而後再生成文件。

自定義字段

咱們能夠經過JsonKey自定義參數進行註釋並自定義參數來自定義各個字段。例如:是否容許字段爲空等。注意,這裏不加任何JsonKey默認容許空json字段。

例如:

這裏的json使用了「-」做爲字段名,而dart中只容許字母數字下劃線做爲變量名。因此咱們必須對它進行特殊處理。@JsonKey(name="Nicehash-CNHeavy")來解析map。

而後再生成文件。

咱們能夠發現,生成文件已經將map中的NicehashCNHeavy替換成了Nicehash-CNHeavy。

泛型狀況

github源碼參考

github.com/Vadaski/Vad…

寫在最後

以上就是Flutter Json自動反序列化的所有內容,文章如有不對之處歡迎各位大牛指正!關於泛型問題如有更好的解決方案但願能在評論區告訴我^-^。

以後我會更新一系列flutter乾貨喜歡的話能夠關注我或者給我一個好評哦!我會更新更有動力的。

從 0 到 1:個人 Flutter 技術實踐 | 掘金技術徵文,徵文活動正在進行中

相關文章
相關標籤/搜索