Dart:处理模型转换 `空字段` 引发的问题
2021-08-23 本文已影响0人
李小轰
引言
在 flutter 开发中,数据源与实体模型的转换是频繁的。将字符串转成 map
,将 map
转成 model
模型,成了家常便饭。转换的方式,我们大概会这么做:
- 根据数据结构,定义模型
{
"code": 0,
"data": [
{"name": "rex"},
{"age": 10}
]
}
///数据模型
class TestModel {
final int code;
final List<Student> data;
TestModel(this.code, this.data);
factory TestModel.fromMap(Map<String, dynamic> value) {
return TestModel(
value["code"] as int,
(value["data"] as List).map((e) => Student.fromMap(e)).toList(),
);
}
}
class Student {
final String name;
final int age;
Student(this.name, this.age);
factory Student.fromMap(Map<String, dynamic> value) {
return Student(
value["name"] as String,
value["age"] as int,
);
}
- 写个 demo 测试一下:
void main() {
String jsonStr = """
{
"data": [
{"name": "rex"},
{"age": 10}
]
}
""";
final Map<String, dynamic> map = new Map<String, dynamic>.from(json.decode(jsonStr));
TestModel model2 = TestModel.fromMap(map);
print(model2.code + 1);
}
-
报错了,报错信息如下:
分析错误
原来,我们与服务端约定好的数据结构里,服务端漏传了一个字段
code
,我们在map
转模型实体的过程中使用as
进行强制类型转换,将null
当做数值类型来进行运算操作,此时发生报错。
处理方案:
我们希望在进行类型转换时,如果map
中字段不存在,则赋值相应类型默认值。(默认值为 false
,0
,""
等等非null
类型)
-
创建工具类处理,代码如下,注释完整,直接 copy 可用:
/// 保证类型解析有默认值,不会出现null强转类型异常
class ParserWithNotNull {
final Object source;
Type _type;
ParserWithNotNull(this.source) {
_type = _asyncType;
}
Type get _asyncType {
if (source == null) {
return null;
}
if (source is num) {
return num;
}
if (source is String) {
return String;
}
if (source is List) {
return List;
}
if (source is Map) {
return Map;
}
if (source is bool) {
return bool;
}
return null;
}
///获取 bool 类型内容
bool get boolValue {
if (_type is bool) {
return source as bool;
} else {
return false;
}
}
///获取 String 类型内容
String get stringValue {
switch (_type) {
case num:
case String:
case bool:
case List:
case Map:
return source.toString();
break;
default:
return "";
break;
}
}
///获取 int 类型内容
int get intValue {
switch (_type) {
case num:
return (source as num).toInt();
break;
case String:
return int.parse(source.toString());
break;
case bool:
return (source as bool) ? 1 : 0;
break;
default:
return 0;
break;
}
}
///获取 double 类型内容
double get doubleValue {
switch (_type) {
case num:
return (source as double).toDouble();
break;
case String:
return double.parse(source.toString());
break;
default:
return 0;
break;
}
}
///获取 List 类型内容
List<ParserWithNotNull> get arrayValue {
if (_type == List) {
return (source as List<dynamic>)
.map((e) => ParserWithNotNull(e))
.toList();
} else {
return List<ParserWithNotNull>();
}
}
///获取 map 类型内容
Map<String, ParserWithNotNull> get mapValue {
if (_type == Map) {
return (source as Map<String, dynamic>)
.map((key, value) => MapEntry(key, ParserWithNotNull(value)));
} else {
return Map<String, ParserWithNotNull>();
}
}
///提供根据 key/index 获取 map 与 list 对应 value 的快捷方法
ParserWithNotNull operator [](Object target) {
ParserWithNotNull result = ParserWithNotNull(null);
if (target is int) {
//从 list 中取值
final list = arrayValue;
if (target < list.length) {
result = list[target];
}
} else {
//从map中取值
final map = mapValue;
if (map.containsKey(target)) {
result = mapValue[target];
}
}
return result;
}
}
使用方式:
- 模型转换的地方修改一下转换方式,保证每个类型都有默认值:
class TestModel {
...省略...
factory TestModel.fromJson(ParserWithNotNull value) {
return TestModel(
value["code"].intValue,
value["data"].arrayValue.map((e) => Student.fromJson(e)).toList(),
);
}
}
class Student {
...省略...
factory Student.fromJson(ParserWithNotNull value) {
return Student(
value["name"].stringValue,
value["age"].intValue,
);
}
}
注意到我们方法的入参不再是 Map
,而是 ParserWithNotNull
,通过ParserWithNotNull
进行数据获取中转,当为 null
时,会直接赋值各类型的默认值,保证非null。
- demo 修改
...省略
final Map<String, dynamic> map = new Map<String, dynamic>.from(json.decode(jsonStr));
final ParserWithNotNull parser = ParserWithNotNull(map);
TestModel model = TestModel.fromJson(parser);
print(model.code + 1);
-
修改点如下:
运行,打印结果为:1 (code变量被赋值默认值 0)