Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Can't deserialize DateTime from Firestore, but locally it works. Both is a DateTime. #454

Closed
Jonas-Sander opened this issue Jul 17, 2018 · 4 comments

Comments

@Jonas-Sander
Copy link

I am trying to deserialize a document from Firestore into my homework object, but I get this error message:

E/flutter ( 7596): [ERROR:topaz/lib/tonic/logging/dart_error.cc(16)] Unhandled exception:
E/flutter ( 7596): Deserializing '[subject, Geschichte, todoUntil, 2018-07-16 04:00:00.000, forUsers, {userID1:...' to 'Homework' failed due to: Deserializing '2018-07-16 04:00:00.000' to 'DateTime' failed due to: type 'DateTime' is not a subtype of type 'int' in type cast
E/flutter ( 7596): #0      BuiltJsonSerializers._deserialize (package:built_value/src/built_json_serializers.dart:151:11)
E/flutter ( 7596): #1      BuiltJsonSerializers.deserialize (package:built_value/src/built_json_serializers.dart:102:18)
E/flutter ( 7596): #2      BuiltJsonSerializers.deserializeWith (package:built_value/src/built_json_serializers.dart:32:12)
E/flutter ( 7596): #3      parseHomework (package:sharezone/models/homework.dart:34:60)
E/flutter ( 7596): #4      main (file:///C:/Users/jonas/AndroidStudioProjects/sharezone/lib/main.dart:19:17)
E/flutter ( 7596): <asynchronous suspension>
E/flutter ( 7596): #5      _startIsolate.<anonymous closure> (dart:isolate/runtime/libisolate_patch.dart:279:19)
E/flutter ( 7596): #6      _RawReceivePortImpl._handleMessage (dart:isolate/runtime/libisolate_patch.dart:165:12)

The thing is that it works locally with DateTime.now but doesn't with the DateTime in the Firestore Document. Here is the Firestore Document returned:
firestore homework document

//THIS WORKS:
  Homework localHomework = new Homework((b) => b
    ..documentID = "ABC"
    ..subject = "Geschichte"
    ..subjectAbbreviation = "GE"
    ..title = "Geschichtshausaufgabe"
    ..todoUntil = DateTime.now() // <- Works
    ..forUsers = MapBuilder({"UserID" : true})
  );

  //THIS DOES NOT WORK:
  DocumentSnapshot docSnap = await Firestore.instance.collection("Homework").document("unitTestDocument_DONT_DELETE").get();
  Homework firestoreHomework = parseHomework(docSnap);

homework.dart

import 'package:built_value/built_value.dart';
import 'package:built_collection/built_collection.dart';
import 'package:built_value/serializer.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'serializers.dart';

part 'homework.g.dart';

//TO RUN: flutter packages pub run build_runner build
//OR: flutter packages pub run build_runner watch

abstract class Homework implements Built<Homework, HomeworkBuilder> {
  @nullable
  String get documentID;
  String get subject;
  String get subjectAbbreviation;
  String get title;
  DateTime get todoUntil;
  @nullable
  String get description;
  BuiltMap<String, bool> get forUsers;

  static Serializer<Homework> get serializer => _$homeworkSerializer;

  Homework._();
  factory Homework([updates(HomeworkBuilder b)]) = _$Homework;
}

Homework parseHomework(DocumentSnapshot homeworkDocument) {
  Homework homeworkWithoutDocumentID = standardSerializers.deserializeWith<Homework>(Homework.serializer, homeworkDocument.data);
  //As the document ID is not in the Map, but an attribute of the Document I'll have to add it manually. Any way to fix this?
  var homeworkWithDocID = homeworkWithoutDocumentID.rebuild((b) => b
    ..documentID = homeworkDocument.documentID);

  assert(homeworkWithDocID.documentID != null);

  return homeworkWithDocID;
}

serializers.dart

library serializers;

import 'package:built_collection/built_collection.dart';
import 'package:built_value/serializer.dart';
import 'package:built_value/standard_json_plugin.dart';
import 'homework.dart';

part 'serializers.g.dart';

@SerializersFor(const [
  Homework,
])
final Serializers serializers = _$serializers;

final standardSerializers =
(serializers.toBuilder()..addPlugin(new StandardJsonPlugin())).build();

I hope it's not too much or litte...
This is my first issue ;), so if I can improve something writing these let me know.

@dave26199
Copy link

There's already an alternative serializer--just need to install it:

https://github.com/google/built_value.dart/blob/master/built_value/lib/iso_8601_date_time_serializer.dart

Apologies for brevity, posted from phone.

@Jonas-Sander
Copy link
Author

@dave26199 Thank you very much for answering, unfortunately I still get an error message.
It is now:

E/flutter ( 8649): Deserializing '[subject, Geschichte, todoUntil, 2018-07-16 04:00:00.000, forUsers, {userID1:...' to 'Homework' failed due to: Deserializing '2018-07-16 04:00:00.000' to 'DateTime' failed due to: type 'DateTime' is not a subtype of type 'String' in type cast
E/flutter ( 8649): #0      BuiltJsonSerializers._deserialize (package:built_value/src/built_json_serializers.dart:151:11)
E/flutter ( 8649): #1      BuiltJsonSerializers.deserialize (package:built_value/src/built_json_serializers.dart:102:18)
E/flutter ( 8649): #2      BuiltJsonSerializers.deserializeWith (package:built_value/src/built_json_serializers.dart:32:12)
E/flutter ( 8649): #3      parseHomework (package:sharezone/models/homework.dart:34:60)
E/flutter ( 8649): #4      main (file:///C:/Users/jonas/AndroidStudioProjects/sharezone/lib/main.dart:31:32)
E/flutter ( 8649): <asynchronous suspension>
E/flutter ( 8649): #5      _startIsolate.<anonymous closure> (dart:isolate/runtime/libisolate_patch.dart:279:19)
E/flutter ( 8649): #6      _RawReceivePortImpl._handleMessage (dart:isolate/runtime/libisolate_patch.dart:165:12)

So now instead of saying
type 'DateTime' is not a subtype of type 'int' in type cast
it say's
type 'DateTime' is not a subtype of type 'String' in type cast

Ich changed the line:

final standardSerializers =
(serializers.toBuilder()..addPlugin(new StandardJsonPlugin())).build();

to

final standardSerializers =
(serializers.toBuilder()..addPlugin(new StandardJsonPlugin())..add(Iso8601DateTimeSerializer())).build();

Did I do something wrong?

@dave26199
Copy link

Ah, I understand now. The problem is that it actually does not need serializing at all, it's already a DateTime. You can do that following the instructions for GeoPoint here:

#417

I.e. add your own "serializer" that does nothing.

Apologies for brevity, sent from phone...

@Jonas-Sander
Copy link
Author

@dave26199 Thank you very much for your help!
Everything working now.

For anybody who has the same issue, here's the solution:

1. Make an own serializer, which does nothing

In my case:
import 'package:built_collection/built_collection.dart';
import 'package:built_value/serializer.dart';

///A Serializer which does basically "nothing".
/// It is used, as a DateTime returned by Firestore is ALREADY a DateTime Object
/// and not just "data" which has to be deserialized into a DateTime.
class DateTimeSerializer implements PrimitiveSerializer<DateTime> {
  final bool structured = false;
  @override
  final Iterable<Type> types = new BuiltList<Type>([DateTime]);
  @override
  final String wireName = 'GeoPoint';

  @override
  Object serialize(Serializers serializers, DateTime geoPoint,
      {FullType specifiedType: FullType.unspecified}) {
    return geoPoint;
  }

  @override
  DateTime deserialize(Serializers serializers, Object serialized,
      {FullType specifiedType: FullType.unspecified}) {
    return serialized as DateTime;
  }
}

2. Add the DateTimeSerializer to your serializers

final serializers =
(_$serializers.toBuilder()..add(DateTimeSerializer())..addPlugin(new StandardJsonPlugin())).build();

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants