The introduction

In FLUTTER development, conversion between data source and entity model is frequent. Turning strings into maps and maps into model models is commonplace. To convert, we would do something like this:

  • Define the model based on the data structure
{
	"code": 0,
	"data": [
        {"name": "rex"},
		{"age": 10}
	]
}
Copy the code
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, ); }Copy the code
  • Write a demo to test it:
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);
}
Copy the code
  • Error message, error message:

Analysis of the error

It turned out that in the data structure we agreed with the server, the server missed a field code, and we used AS to perform cast conversion during the process of map to model entity, and null was used as a numerical type for operation. At this time, an error occurred.


Treatment scheme:

We want to assign the corresponding type default value if the map field does not exist during the type conversion. (Defaults to false, 0, “”, etc. Non-null)

  • Create tool class processing, code as follows, complete annotation, direct copy available:

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 get boolValue {if (_type is bool) {return source as bool; } else { return false; String get stringValue {switch (_type) {case num: case String: case bool: case List: case Map: return source.toString(); break; default: return ""; break; 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 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<ParserWithNotNull> get arrayValue {if (_type == List) {return (source List<dynamic>) .map((e) => ParserWithNotNull(e)) .toList(); } else { return List<ParserWithNotNull>(); 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>(); ParserWithNotNull operator [](Object target) {ParserWithNotNull operator [](Object target) {ParserWithNotNull result = ParserWithNotNull(null); If (target is int) {// Final list = arrayValue; if (target < list.length) { result = list[target]; }} else {// Final map = mapValue; if (map.containsKey(target)) { result = mapValue[target]; } } return result; }}Copy the code

Usage:

  • Modify the conversion mode to ensure that each type has a default value:
class TestModel { ... Omit... factory TestModel.fromJson(ParserWithNotNull value) { return TestModel( value["code"].intValue, value["data"].arrayValue.map((e) => Student.fromJson(e)).toList(), ); } } class Student { ... Omit... factory Student.fromJson(ParserWithNotNull value) { return Student( value["name"].stringValue, value["age"].intValue, );  }}Copy the code

Note that the input parameter of our method is no longer Map, but ParserWithNotNull, which is used to obtain data through ParserWithNotNull. When null, the default values of each type are directly assigned to ensure non-NULL.

  • The demo to modify
. Omit 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);Copy the code
  • Modification points are as follows:

Run and print: 1 (code variable is assigned a default value of 0)