在 Flutter 中使用JSON 现在网上已经有很多教程了。此篇文章用于记录和学习使用
参考 Flutter中文网 - JSON
本文介绍了使用JSON的两个常规策略:
- 手动序列化和反序列化
- 通过代码生成自动序列化和反序列化
不同的项目具有不同的复杂度和场景。对于较小项目,使用代码生成器可能会过度。对于具有多个JSON model的复杂应用程序,手动序列化可能会比较重复,并会很容易出错。
小项目手动序列化
手动JSON序列化是指使使用dart:convert
中内置的JSON解码器。它将原始JSON字符串传递给JSON.decode() 方法,然后在返回的Map<String, dynamic>
中查找所需的值。 它没有外部依赖或其它的设置,对于小项目很方便。
当您的项目变大时,手动编写序列化逻辑可能变得难以管理且容易出错。如果您在访问未提供的JSON字段时输入了一个错误的字段,则您的代码将会在运行时会引发错误。
如果您的项目中JSON model并不多,并且希望快速测试一下,那么手动序列化可能会很方便。有关手动序列化的示例,请参考这里。
在大中型项目中使用代码生成
代码生成功能的JSON序列化是指通过外部库为您自动生成序列化模板。它需要一些初始设置,并运行一个文件观察器,从您的model类生成代码。 例如,json_serializable和built_value就是这样的库。
这种方法适用于较大的项目。不需要手写,如果访问JSON字段时拼写错误,这会在编译时捕获的。代码生成的不利之处在于它涉及到一些初始设置。另外,生成的源文件可能会在项目导航器会显得混乱。
使用代码生成库序列化JSON
这里主要介绍json_serializable,大概步骤:
- 1 创建一个实体类
- 2 生成代码:来让build runner生成序列化代码。运行完成后文件夹下会出现一个xxx.g.dart文件,这个文件就是生成后的文件。
- 3 代理实现:把fromJson和toJson操作代理给上面生成出来的类
json_serializable 借助了build_runner 和json_annotation 库,来自动生成fromJson/toJson函数内容。(关于使用build_runner生成代码的原理,参考Flutter路由管理代码这么长长长长长,阿里工程师怎么高效解决?(实用))
1、添加项目依赖
dependencies:
flutter:
sdk: flutter
dio: ^1.0.14
json_annotation: ^2.0.0
dev_dependencies:
flutter_test:
sdk: flutter
build_runner: ^1.1.3
json_serializable: ^2.0.1
2、创建实体类
JSON 格式
{
"list1": [
{
"rown": 1,
"ID": "cc72a5fb-659a-4941-b56d-1f0f734667b3",
"DAH": "201806060000669",
"XMMC": "晶泽·云玺(一期)",
"XMDZ": "南湖生态城B-05地块",
"BSM": 8635,
"XSMC": "晶泽·云玺",
"XMJJ": 8300.05,
"QY": 510302,
"FTime": "2018-09-06",
"ZTS": 752,
"PIC": "../../images/xmimg/b72c1ecd-2f0b-46a0-b226-2440817e9f4d-20180607093242.jpg"
}
]
}
创建实体类
在这里可以借助工具 为了便利使用 json_serializable库
将 JSON 拷进去会自动生成对应实体类(据说复杂的格式可能会有问题,需要自己斟酌)
import 'package:json_annotation/json_annotation.dart';
part 'newhouse.g.dart';
@JsonSerializable()
class newhouse extends Object {
@JsonKey(name: 'list1')
List<List1> list1;
newhouse(this.list1,);
factory newhouse.fromJson(Map<String, dynamic> srcJson) => _$newhouseFromJson(srcJson);
Map<String, dynamic> toJson() => _$newhouseToJson(this);
}
@JsonSerializable()
class List1 extends Object {
@JsonKey(name: 'rown')
int rown;
@JsonKey(name: 'ID')
String iD;
@JsonKey(name: 'DAH')
String dAH;
@JsonKey(name: 'XMMC')
String xMMC;
@JsonKey(name: 'XMDZ')
String xMDZ;
@JsonKey(name: 'BSM')
int bSM;
@JsonKey(name: 'XSMC')
String xSMC;
@JsonKey(name: 'XMJJ')
double xMJJ;
@JsonKey(name: 'QY')
int qY;
@JsonKey(name: 'FTime')
String fTime;
@JsonKey(name: 'ZTS')
int zTS;
@JsonKey(name: 'PIC')
String pIC;
List1(this.rown,this.iD,this.dAH,this.xMMC,this.xMDZ,this.bSM,this.xSMC,this.xMJJ,this.qY,this.fTime,this.zTS,this.pIC,);
factory List1.fromJson(Map<String, dynamic> srcJson) => _$List1FromJson(srcJson);
Map<String, dynamic> toJson() => _$List1ToJson(this);
}
生成序列化模板
有两种运行代码生成器的方法:
一次性生成
通过在我们的项目根目录下运行flutter packages pub run build_runner build,我们可以在需要时为我们的model生成json序列化代码。 这触发了一次性构建,它通过我们的源文件,挑选相关的并为它们生成必要的序列化代码。
虽然这非常方便,但如果我们不需要每次在model类中进行更改时都要手动运行构建命令的话会更好。
持续生成
使用watcher可以使我们的源代码生成的过程更加方便。它会监视我们项目中文件的变化,并在需要时自动构建必要的文件。我们可以通过flutter packages pub run
build_runner watch在项目根目录下运行来启动watcher。
只需启动一次观察器,然后并让它在后台运行,这是安全的。
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'newhouse.dart';
// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************
newhouse _$newhouseFromJson(Map<String, dynamic> json) {
return newhouse((json['list1'] as List)
?.map((e) => e == null ? null : List1.fromJson(e as Map<String, dynamic>))
?.toList());
}
Map<String, dynamic> _$newhouseToJson(newhouse instance) =>
<String, dynamic>{'list1': instance.list1};
List1 _$List1FromJson(Map<String, dynamic> json) {
return List1(
json['rown'] as int,
json['ID'] as String,
json['DAH'] as String,
json['XMMC'] as String,
json['XMDZ'] as String,
json['BSM'] as int,
json['XSMC'] as String,
(json['XMJJ'] as num)?.toDouble(),
json['QY'] as int,
json['FTime'] as String,
json['ZTS'] as int,
json['PIC'] as String);
}
Map<String, dynamic> _$List1ToJson(List1 instance) => <String, dynamic>{
'rown': instance.rown,
'ID': instance.iD,
'DAH': instance.dAH,
'XMMC': instance.xMMC,
'XMDZ': instance.xMDZ,
'BSM': instance.bSM,
'XSMC': instance.xSMC,
'XMJJ': instance.xMJJ,
'QY': instance.qY,
'FTime': instance.fTime,
'ZTS': instance.zTS,
'PIC': instance.pIC
};
使用json_serializable模型
反序列化JSON字符串
Map houseMap = json.decode(json);
var house = new newhouse.fromJson(houseMap);
序列化
String json = JSON.encode(house );
最近咸鱼团队也出了自己的文章如何在Flutter上优雅地序列化一个对象(实用)
在文章中指出了 json_serializable 的不足之处:
- 使用起来有些繁琐,多引入了一个类
- 很重要的一点是,大量的使用"as"会给性能和最终产物大小产生不小的影响。实际上闲鱼内部的《flutter编码规范》中,是不建议使用"as"的。(对包大小的影响可以参见三笠同学的文章,同时dart linter也对as的性能影响有所描述)
并引入了一种正经的方式(哈哈):fish-serializable 详细内容可见文章
。不过暂时还未开源,开源之后再做更新,可能会是一种更好的方式。