如今很难想象移动应用程序不须要与后台交互或者存储结构化数据。如今开发,数据传输方式基本都是用JSON
,在Flutter
中是没有GSON/Jackson/Moshi
这些库,由于这些库须要运行时反射,在Flutter
是禁用的。运行时反射会干扰Dart
的_tree shaking_。使用_tree shaking_,能够在发版是"去除"未使用的代码,来优化软件的大小。因为反射会默认使用全部代码,所以_tree shaking_会很难工做,这些工具没法知道哪些widget
在运行时未被使用,所以冗余代码很难剥离,使用反射时,应用尺寸没法轻松进行优化,虽然不能在Flutter
使用运行时反射,但有些库提供了类型简单易用的API
,但它们是基于代码生成的。下面学学在Flutter
中如何操做JSON
数据的使用JSON
有两个常规策略:json
JSON model
的复杂应用程序,手动序列化可能会比较繁琐,且容易出错。Flutter
中基本的JSON序列化很是简单,Flutter
有一个内置的dart:convert
库,其中包含一个简单的JSON解码器和编码器。下面简单实现一下:安全
首先记得导库:函数
import 'dart:convert';
而后根据字符串解析:工具
//内连序列化JSON decodeJson() { var data= '{"name": "Knight","email": "Knight@163.com"}'; Map<String,dynamic> user = json.decode(data); //输出名字 print("Hello,my name is ${user['name']}"); //输出邮箱 print("Hello,This is my email ${user['email']}"); }
结果输出:优化
I/flutter ( 5866): Hello,my name is Knight
I/flutter ( 5866): Hello,This is my email Knight@163.com
这样,能够得到咱们想要的数据了,我以为这种方法很实用又能简单理解,可是不幸的是,JSON.decode()
仅返回一个Map<String,dynamci>
,这意味着当直到运行才知道值的类型,这种方法会失去大部分静态类型语言特性:类型安全、自动补全和编译时异常。这样的话,代码变得很是容易出错,就好像上面咱们访问name
字段,打字打错了,达成namr
。可是这个JSON
在map结构中,编译器不知道这个错误的字段名(编译时不会报错)。为了解决所说的问题,模型类中序列化JSON的做用出来了。ui
经过引入一个简单的模型类(model class)来解决前面提到的问题,创建一个User
类,在类内部有两个方法:this
User.fromJson
构造函数,用于从一个map构造出一个User
实例map structuretoJson
方法,将User
实例化一个map 这样调用的代码就具备类型安全、自动补全和编译时异常,当拼写错误或字段类型视为其余类型,程序不会经过编译,那就避免运行时崩溃。新建一个model文件夹,用来放实体,在其文件下新建User.dart
:编码
class User { final String name; final String email; User(this.name, this.email); User.fromJson(Map<String, dynamic> json) : name = json['name'], email = json['email']; Map<String, dynamic> toJson() => { 'name': name, 'email': email, }; }
调用以下:spa
import 'model/User.dart';//记得添加 .... //使用模型类反序列化 decodeModelJson(){ var data= '{"name": "Knight","email": "Knight@163.com"}'; Map userMap = json.decode(data); var user = new User.fromJson(userMap); //打印出名字 print("Hello,my name is ${user.name}"); //打印出邮箱 print("Hello,my name is ${user.email}"); }
把序列化逻辑到移到模型自己内部,采用这种方法,反序列化数据就很简单了。序列化一个user,只是将User
对象传递给该JSON.encode
方法:命令行
//序列化一个user encodeModelJson(){ var user = new User("Knight","Knight163.com"); String user_json = json.encode(user); print(user_json); }
结果输出:
I/flutter ( 6684): {"name":"Knight","email":"Knight163.com"}
下面使用json_serializable package
包,它是一个自动化的源代码生成器,能够为开发者生成JSON序列化魔板。
要包含json_serializable
到项目中,须要一个常规和两个开发依赖项,开发依赖项是不包含在应用程序源代码中的依赖项:
dependencies: # Your other regular dependencies here json_annotation: ^2.0.0 dev_dependencies:-->开发依赖项 # Your other dev_dependencies here build_runner: ^1.1.3 -->最新版本1.2.8 由于我sdk版本比较低 因此用低版本 json_serializable: ^2.0.2
有两种运行代码生成器的方法:
flutter packages pub run build_runner build
,能够在须要为咱们的model
生成json
序列化代码。这触发一次性构建,它经过源文件,挑选相关的并为它们生成必要的序列化代码。这个很是方便,可是若是咱们不须要每次在model类中进行更改都要手动运行构建命令的话会更好。flutter packages pub run build_runner watch
在项目根目录运行启动_watcher_,只需启动一次观察器,而后并让它在后台运行,这是安全的。将上面的User.dart
修改为下面:
import 'package:json_annotation/json_annotation.dart'; part 'User.g.dart';-->一开始爆红 //这个标注是告诉生成器,这个类是须要生成Model类的 @JsonSerializable() class User{ User(this.name, this.email); String name; String email; factory User.fromJson(Map<String, dynamic> json){--->一开始爆红 return _$UserFromJson(json); } Map<String, dynamic> toJson() { --->一开始爆红 return _$UserToJson(this); } }
下面就用一次性生成命令,在项目根目录打开命令行执行:
最后发现会在当前目录生成User.g.dart
文件:
里面的内容能够本身去看看看,就是反序列化/序列化的操做。注意:没生成User.g.dart
执行多几回命令便可。最后经过json_serializable
方式反序列化JSON
字符串,不须要对先前代码修改:
var data= '{"name": "Knight","email": "Knight@163.com"}'; Map userMap = json.decode(data); var user = new User.fromJson(userMap); //打印出名字 print("Hello,my name is ${user.name}"); //打印出邮箱 print("Hello,my name is ${user.email}");
var user = new User("Knight","Knight163.com"); String user_json = json.encode(user); print(user_json);
结果是跟上面同样,不过这种方式额外多了生成一个文件...