Closed ScottPierce closed 4 years ago
It seems to be unhappy with this model:
@jsonSerializable
class Registration {
final String id;
final String type;
final Person student;
final List<Person> guardians;
final Map<String, String> addresses;
final bool isRead;
final bool isArchived;
Registration(this.id, this.type, this.student, {this.guardians = const [], this.addresses = const {}, this.isRead = false, this.isArchived = false});
Registration copy({String id, String type, Person student, List<Person> guardians, Map<String, String> addresses, bool isRead, bool isArchived}) {
return Registration(
id ?? this.id,
type ?? this.type,
student ?? this.student,
guardians: guardians ?? this.guardians,
addresses: addresses ?? this.addresses,
isRead: isRead ?? this.isRead,
isArchived: isArchived ?? this.isArchived,
);
}
@override
String toString() {
return 'Registration{id: $id, type: $type, student: $student, guardians: $guardians, address: $addresses, isRead: $isRead, isArchived: $isArchived}';
}
}
So I'm seeing several layers of problems. Going to try and summarize them here.
I have a function to grab the firebase firestore document and try to deserialize it. Here's what it looks like:
class FirebaseMapper {
static T fromDocument<T>(DocumentSnapshot document) {
final data = document.data.cast<String, dynamic>();
data['id'] = document.documentID;
return JsonMapper.fromJson<T>(data);
}
FirebaseMapper._();
}
Only, when I do that, I'm seeing this error:
E/flutter (25770): [ERROR:flutter/lib/ui/ui_dart_state.cc(157)] Unhandled Exception: type '_InternalLinkedHashMap<dynamic, dynamic>' is not a subtype of type 'Map<String, dynamic>'
E/flutter (25770): null
E/flutter (25770): [ERROR:flutter/shell/common/shell.cc(199)] Dart Error: Unhandled exception:
E/flutter (25770): type '_InternalLinkedHashMap<dynamic, dynamic>' is not a subtype of type 'Map<String, dynamic>'
E/flutter (25770): #0 _rootHandleUncaughtError.<anonymous closure> (dart:async/zone.dart:1114:29)
E/flutter (25770): #1 _microtaskLoop (dart:async/schedule_microtask.dart:43:21)
E/flutter (25770): #2 _startMicrotaskLoop (dart:async/schedule_microtask.dart:52:5)
So I try to work around it using this:
static T fromDocument<T>(DocumentSnapshot document) {
final data = document.data.cast<String, dynamic>();
data['id'] = document.documentID;
final json = jsonEncode(data);
return JsonMapper.fromJson<T>(json);
}
Then I get this error from this Registration
model:
E/flutter (25770): [ERROR:flutter/shell/common/shell.cc(199)] Dart Error: Unhandled exception:
E/flutter (25770): type '_ImmutableMap<dynamic, dynamic>' is not a subtype of type 'Map<String, String>'
E/flutter (25770): #0 _rootHandleUncaughtError.<anonymous closure> (dart:async/zone.dart:1114:29)
E/flutter (25770): #1 _microtaskLoop (dart:async/schedule_microtask.dart:43:21)
E/flutter (25770): #2 _startMicrotaskLoop (dart:async/schedule_microtask.dart:52:5)
@jsonSerializable
class Registration {
final String id;
final String type;
final Person student;
final List<Person> guardians;
final Map<String, String> addresses;
final bool isRead;
final bool isArchived;
Registration(this.id, this.type, this.student, {this.guardians = const [], this.addresses = const {}, this.isRead = false, this.isArchived = false});
Registration copy({String id, String type, Person student, List<Person> guardians, Map<String, String> addresses, bool isRead, bool isArchived}) {
return Registration(
id ?? this.id,
type ?? this.type,
student ?? this.student,
guardians: guardians ?? this.guardians,
addresses: addresses ?? this.addresses,
isRead: isRead ?? this.isRead,
isArchived: isArchived ?? this.isArchived,
);
}
@override
String toString() {
return 'Registration{id: $id, type: $type, student: $student, guardians: $guardians, address: $addresses, isRead: $isRead, isArchived: $isArchived}';
}
}
I change the Registration
model to use final Map<dynamic, dyanmic> addresses;
to try to work around it, and then I get this error:
E/flutter (26131): [ERROR:flutter/lib/ui/ui_dart_state.cc(157)] Unhandled Exception: type 'List<Object>' is not a subtype of type 'List<Person>'
E/flutter (26131): null
I can't really work around that one. Here is the json from final json = jsonEncode(data);
{"address":"555 Blah Blah Ct, Charleston, TX 55555","student":{"firstName":"Scott","lastName":"Pierce","phone":"5555555555","email":"test@gmail.com"},"isArchived":false,"isRead":true,"guardians":[{"firstName":"Jarjar","lastName":"Binks","phone":"5555555555","email":"test2@gmail.com"}],"type":"Moo moo","id":"VqUt06fi9IekRSag0DdR"}
type 'List<Object>' is not a subtype of type 'List<Person>'
For the list issue you'll find an example at https://github.com/k-paxian/dart-json-mapper#iterable-types
You should try this approach
JsonMapper.registerValueDecorator<List<Person>>(
(value) => value.cast<Person>());
JsonMapper.registerValueDecorator<Map<String, String>>(
(value) => value.cast<String, String>());
// below you could try to serialize/deserialize stuff
class FirebaseMapper {
static T fromDocument<T>(DocumentSnapshot document) {
// as far as I know 'document.data' is already Map<String, dynamic>, so
return JsonMapper.fromMap<T>(data);
}
FirebaseMapper._();
}
and let me know the result please.
And for the copy, you could use JsonMapper.clone()
someday.
You are right that document.data
is Map<String, dynamic>
, the problem seems to be coming from internal Map
s within that.
I'll try that when I can revert back to this library to give it a shot, and let you know the results.
Out of curiosity (again I'm new to Dart), what are the limitations of dart that this value decorator approach is necessary?
It's all about speed and performance. The point is that at the moment it's not possible to dynamically instantiate an iterable of a certain strong type. Just because iterable types has to be defined in compile time, so compiler will have a chance to optimize them, not sure about all the tiny details, but the general direction is correct.
At runtime you are allowed to use cast mechanism only. But to use this approach you should hardcode the exact iterable type somewhere in the code anyway.
I don't like this approach as well, maybe in future I'll be able to transfer this responsibility to generated code.
More info and examples you could find here https://github.com/k-paxian/dart-json-mapper#iterable-types
Feel free to re-open, when you are ready to continue.
I'm using this library to deserialize Firestore documents, and I'm seeing the following error whenever I try to call
JsonMapper.fromMap<T>(data);
I was seeing a similar issue with
json_serializable
, but I was able to fix it with an option they provided in mybuild.yaml
: https://pub.dev/documentation/json_annotation/latest/json_annotation/JsonSerializable/anyMap.html