[Flutter] 08-Flutter中的Json转Model

背景: 在开发中,服务端通常返回Json数据,我们需要将Json数据转模型对象来使用。一般情况下,我们会使用一些第三方库来动态转化Model,但是Flutter中没有像Java的Gson/Jackson这类Json序列化类库,因为Flutter中禁用运行时反射。官方解释是运行时反射会干扰Dart的tree shaking,使用tree shaking可以在release版中去除未使用的代码,这可以显著优化应用程序的大小。由于反射会默认应用到所有代码,因此tree shaking会很难工作,因为在启用反射时很难知道哪些代码未被使用,因此冗余代码很难剥离,所以Flutter中禁用了Dart的反射功能,而正因如此也就无法实现动态转化Model的功能。

在此基础上,接下来我们看下Flutter中还有哪几种Json转模型的方式:

一. 手动转化

在上篇[Flutter] 07-Flutter中反序列化Json已经通过6个示例分析过了, 这里不再讨论。

二. json_serializable

json_serializable是dart官方推荐和提供的JSON转Model的方式:

  • 一个自动化源代码生成器来为你生成 JSON 序列化数据模板;
  • 由于序列化数据代码不再需要手动编写或者维护,你可以将序列化 JSON 数据在运行时的异常风险降到最低;

第1步:添加相关的依赖

依赖分为项目依赖(dependencies),开发依赖(dev_dependencies),在pubspec.yaml中添加如下依赖:

dependencies:
  json_annotation:^3.0.1

dev_dependencies:
  json_serializable:^3.2.5
  build_runner:^1.8.0
  • 注意:添加后需要执行flutter pub get确保我们的项目中有这些依赖。
  • 注意:yaml配置文件对于缩进要求十分严格,下面的build_runnerjson_serializable应该是与flutter_test平级的,千万不要写在flutter_test缩进后,这样它会认为这两个是flutter_test的子集目录!

由于很多朋友在这一步遇到了问题,这里贴出源码:

第2步:以json_serializable 的方式创建模型类

  • 根据下面简单Json数据创建模型类:
final jsonInfo = {
  "nickname": "coderTao",
  "age": 20,
  "courses": ["政治", "高数", "英语"],
  "register_date": "2018-2-22",
  "computer": {
    "brand": "MackBook",
    "price": 9999
  }
};
  • User类的代码:
// 1.import 导入json_annotation.dart
import 'package:json_annotation/json_annotation.dart';
import 'computer_model.dart';

// 2.user.g.dart 将在我们运行生成命令后json_serializable帮我们自动生成.g.dart文件,在未执行命令前该行可能会报错
part 'user_model.g.dart';

// 3.这个标注是告诉生成器,这个类是需要生成Model类的
@JsonSerializable()
class User {
  String name;
  int age;
  //显式关联JSON字段名与Model属性的对应关系,
  // 如下将属性registerDate和register_date字段关联
  @JsonKey(name: "register_date")
  String registerDate;
  List<String> courses;
  Computer computer;
  // 4.必须的构造方法
  User(this.name, this.age, this.registerDate, this.courses, this.computer);
  // 5.必须有的对应工厂构造器
  factory User.fromJson(Map<String, dynamic> json) => _$UserFromJson(json);
  Map<String, dynamic> toJson() => _$UserToJson(this);

  //这里 toString方法不是必须的, 只是用测试数据
  @override
  String toString() {
    return'User{name: $name, age: ${age}, registerDate: $registerDate, courses: $courses, computer: $computer}';
  }
}
  • Computer类的代码:
// 1.import 导入json_annotation.dart
import 'package:json_annotation/json_annotation.dart';

// 2.computer.g.dart 将在我们运行生成命令后json_serializable帮我们自动生成.g.dart文件,在未执行命令前该行可能会报错
part 'computer.g.dart';

// 3.这个标注告诉json_serializable哪一个类需要进行转换生成Model类
@JsonSerializable()
class Computer {
  String brand;
  double price;
  //4.必须的构造方法
  Computer(this.brand, this.price);
  //5.必须有的对应工厂构造器
  factory Computer.fromJson(Map<String, dynamic> json) => _$ComputerFromJson(json);
  Map<String, dynamic> toJson() => _$ComputerToJson(this);
    
  //这里 toString方法不是必须的, 只是用测试数据
  @override
  String toString() {
    return'Computer{brand: $brand, price: $price}';
  }
}

最后总结一下以json_serializable 的方式创建模型类必须5步:

  • 1.import 导入json_annotation.dart
import 'package:json_annotation/json_annotation.dart';
  • 2.json_serializable根据当前类,以part 类名.g.dart格式生成的文件。
    以user.dart为例如下:
part 'user.g.dart';
  • 3.在class上标注 @JsonSerializable() 告诉json_serializable哪一个类需要进行转换生成Model类。
  • 4.创建必须的构造方法。
  • 5.创建必须的对应的工厂构造器。

备注1:
第五步实际就是创建两个方法:

  • 提供一个工厂构造方法User.fromJson,该方法实际调用生成文件的UserFromJson方法进行反序列化。
  • 提供一个toJson()序列化对象的方法,实际调用生成文件的_$UserToJson()方法,并将调用对象解析生成Map<String ,dynamic>。

备注2:

  • _$UserFromJson(json) : 它接收了一个map:Map<String, dynamic>,并将这个Map里的值映射为我们所需要的实体类对象。我们就可以使用这个方法,将存有json数据的map转化为我们需要的实体类对象。
  • _$UserToJson(this): 将调用此方法的对象直接根据字段映射成Map。
    而这两个都是私有方法,part让两个文件共享作用域与命名空间,所以我们需要将生成的方法暴露给外部。

备注3:
UserFromJson(json)ToJson()调用方法,在未执行生成对应的.g.dart文件指令前该行可能会报错。

part 'computer.g.dart';part 'user.g.dart'; ,在未执行生成对应的.g.dart文件指令前该行可能会报错。

备注4:
toString方法不是必须的,只用来打印输出进行测试。

第3步:生成对应的.g.dart文件指令

该操作有两种指令:一次性生成指令和 持续性生成指令。

一次性生成指令

在项目终端运行下面的指令:

flutter pub run build_runner build
  • 该指令是一次性生成JSON序列化的代码。 该指令通过我们的源文件,找出需要生成Model类的源文件(包含@JsonSerializable标注的)来生成对应的.g.dart文件。建议将所有Model类放在一个单独的目录下,然后在该目录下执行命令。

持续性生成指令

如果感觉每次更改Model时都需要执行一次性生成指令比较繁琐,这时可以使用下面的持续生成指令:

flutter pub run build_runner watch

在项目根目录下运行该指令后会启动观察器, 观察器可以监视我们项目中文件的变化,并在需要时自动构建必要的文件。只需启动一次观察器,然后它就会在后台运行,这种方式也很安全。

第4步:测试并打印

final jsonInfo = {
  "nickname": "coderTao",
  "age": 20,
  "courses": ["政治", "高数", "英语"],
  "register_date": "2018-2-22",
  "computer": {
    "brand": "MackBook",
    "price": 9999
  }
};

final user = User.fromJson(jsonInfo);
print(user);

三. 网页转换

app.quicktype.io 是一个将JSON转换成模型类的工具网站,目前来看支持大部分常用语言,并且灵活的可选项也非常多:

优点: 这种方式操作起来会比使用json_serializable操作起来更简便一些,并且带下划线字段会自动转换为驼峰命名的属性名。
缺点: 如果数据过于复杂的话,在生成的时候可能会少了某一个类,并且不能进行父类抽取。

四. 编辑器插件

目前Android Studio(或IntelliJ)有几个插件,可以将json文件转成Model类,但插件质量参差不齐,甚至还有一些有抄袭嫌疑,故笔者在此不做优先推荐,读者有兴趣可以自行了解。

Json转Model几种方式总结:

  • 手动序列化JSON:比较麻烦,效率低,但新手还是多做尝试和了解比较好。
  • json_serializable:效率高,watch很好用。
  • 工具网站:效率高,更多功能可选。

总体推荐使用后两种,可以大大提升开发效率,不用埋头去搞一些重复的序列化工作。

由于笔者水平有限,文中如果有错误的地方,或者有更好的方法,还望大神指出。
附上本文的所有 demo 下载链接,【GitHub】
如果你看完后觉得对你有所帮助,还望在 GitHub 上点个 star。赠人玫瑰,手有余香。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,013评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,205评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,370评论 0 342
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,168评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,153评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,954评论 1 283
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,271评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,916评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,382评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,877评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,989评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,624评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,209评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,199评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,418评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,401评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,700评论 2 345