Как да разреша тип „Timestamp“ не е подтип на тип „String“ в тип преобразуване

Искам да извличам срещи от Firestore и да ги картографирам в следния Meeting модел:

part 'meeting.g.dart';

@JsonSerializable(explicitToJson: true)
class Meeting {
  String id;
  DateTime date;

  Meeting(this.id, this.date);

  factory Meeting.fromJson(Map<String, dynamic> json) {

    return _$MeetingFromJson(json);
  }

  Map<String, dynamic> toJson() => _$MeetingToJson(this);
}

Документите се извличат от Firestore и след това fromJson се извиква на итерируемия, но се хвърля изключение:

type 'Timestamp' is not a subtype of type 'String' in type cast

Когато вляза в генериран meeting.g.dart, този ред причинява грешката

json['date'] == null ? null : DateTime.parse(json['date'] as String)

За да заобиколя проблема, опитах да променя от DateTime на Timestamp в модела, но след това се показва следната грешка при компилиране:

Error running JsonSerializableGenerator
Could not generate `fromJson` code for `date`.
None of the provided `TypeHelper` instances support the defined type.

Бихте ли ми казали как решавате този проблем? Има ли друг предпочитан начин за комбиниране на Firebase и проект Flutter с помощта на json_serializable за JSON сериализация? Може би дори да замените използването на json_serializable?


person Reed    schedule 21.03.2020    source източник
comment
Опитахте ли да замените DateTime.parse(json['date'] as String). с DateTime.parse(json['date'].toString())   -  person Gabe    schedule 22.03.2020
comment
Не мисля, че е добра практика да редактирате генерирани файлове. Ще трябва да правя това всеки път, когато моделът се промени, защото meeting.g.dart ще бъде генериран отново.   -  person Reed    schedule 22.03.2020
comment
Може да се наложи да сериализирате json ръчно, погледнете тази публикация stackoverflow.com/a/58309472/9609442   -  person Gabe    schedule 22.03.2020
comment
В приложението в производство анализирам дата до низ, преди да го кача, след което го конвертирам обратно в дата и час, когато го извлека.   -  person i6x86    schedule 22.03.2020
comment
Какво получавате в json['date']? Дали е милисекундиSinceEpoch или dateString?   -  person Suman Maharjan    schedule 22.03.2020
comment
Можете ли да опитате със следното: json['date'].toDate() и да ме уведомите дали работи?   -  person Nibrass H    schedule 23.03.2020
comment
Как дефинирахте въведено поле за дата в модела на dart? Любезно предложете. Благодаря.   -  person Kamlesh    schedule 15.06.2021


Отговори (3)


Използвайте JsonConverter

class TimestampConverter implements JsonConverter<DateTime, Timestamp> {
  const TimestampConverter();

  @override
  DateTime fromJson(Timestamp timestamp) {
    return timestamp.toDate();
  }

  @override
  Timestamp toJson(DateTime date) => Timestamp.fromDate(date);
}

@JsonSerializable()
class User{
  final String id;
  @TimestampConverter()
  final DateTime timeCreated;

  User([this.id, this.timeCreated]);

  factory User.fromSnapshot(DocumentSnapshot documentSnapshot) =>
      _$UserFromJson(
          documentSnapshot.data..["_id"] = documentSnapshot.documentID);

  Map<String, dynamic> toJson() => _$UserToJson(this)..remove("_id");
}
person Junsu Lee    schedule 19.06.2020
comment
Работи като чар! Благодаря. Това също е съвместимо с freezed. Други решения по-горе прекалено усложняват проблема. - person Alex Hartford; 10.09.2020
comment
Как става това със замразено? - person Valentin Seehausen; 16.04.2021
comment
@AlexHartford ? - person Valentin Seehausen; 18.04.2021
comment
@ValentinSeehausen Можете да използвате декоратора @TimestampConverter() със замразен клас точно както можете с JsonSerializable клас. Работи по абсолютно същия начин. - person Alex Hartford; 19.04.2021
comment
@AlexHartford Благодаря за отговора. Тествахте ли го с текущите, nullsafe версии? Някак си не ми се получи. - person Valentin Seehausen; 19.04.2021
comment
@ValentinSeehausen Да, можете ли да отворите въпрос и да публикувате своя код? Ще се радвам да разгледам. - person Alex Hartford; 20.04.2021
comment
Хей @AlexHartford, благодаря за подкрепата. Копирах въпрос в GitHub и го публикувах тук. Очаквам вашите коментари. - person Valentin Seehausen; 20.04.2021
comment
Как дефинирахте въведеното поле за дата/време в модела на dart с NULL Safety? Любезно предложете. Благодаря. - person Kamlesh; 15.06.2021

Благодаря на @Reed, че посочи правилната посока. При предаване на DateTime стойност към FireStore изглежда няма проблеми за firebase да приеме тази стойност като Timestamp, но когато я върне обратно, тя трябва да бъде обработена правилно. Както и да е, ето пример, който работи и в двете посоки:

import 'package:cloud_firestore/cloud_firestore.dart'; //<-- dependency referencing Timestamp
import 'package:json_annotation/json_annotation.dart';

part 'test_date.g.dart';

@JsonSerializable(anyMap: true)
class TestDate {

  @JsonKey(fromJson: _dateTimeFromTimestamp, toJson: _dateTimeAsIs)
  final DateTime theDate; 


  TestDate({this.theDate,});

   factory TestDate.fromJson(Map<String, dynamic> json) {     
     return _$TestDateFromJson(json);
   } 
  Map<String, dynamic> toJson() => _$TestDateToJson(this);

  static DateTime _dateTimeAsIs(DateTime dateTime) => dateTime;  //<-- pass through no need for generated code to perform any formatting

// https://stackoverflow.com/questions/56627888/how-to-print-firestore-timestamp-as-formatted-date-and-time-in-flutter
  static DateTime _dateTimeFromTimestamp(Timestamp timestamp) {
    return DateTime.parse(timestamp.toDate().toString());
  }
}
person mike123    schedule 29.03.2020

Решение #1

Използвайте функции за преобразуване toJson и fromJson, както в следния пример: https://github.com/dart-lang/json_serializable/blob/master/example/lib/example.dart

Предимствата на решението са, че не е необходимо да кодирате твърдо имена на свойства

Решение #2

След като прочетете https://github.com/dart-lang/json_serializable/issues/351, промених Meeting.fromJson и сега работи според очакванията:

  factory Meeting.fromJson(Map<String, dynamic> json) {
    json["date"] = ((json["date"] as Timestamp).toDate().toString());
    return _$MeetingFromJson(json);
  }

json["date"] е Timestamp по подразбиране, преобразувам го в String, преди да достигне до генерирания десериализатор, така че да не се срива, когато се опита да изпълни json["date"] as String

Въпреки това, това решение не ми харесва много, защото трябва да кодирам твърдо името на свойството и няколко типа, но засега това решение ще е достатъчно добро.

Алтернатива би била да изпробвате https://pub.dev/packages/built_value за serialiazion, което се препоръчва в техния блог https://flutter.dev/docs/development/data-and-backend/json

person Reed    schedule 23.03.2020
comment
Радвам се, че помогна. Опитвали ли сте решението от този пример github.com/ dart-lang/json_serializable/blob/master/example/lib/? - person Reed; 27.03.2020
comment
Трябваше да отида с @JsonKey(fromJson: _dateTime, toJson: _dateTime) просто да оставя json_annotation библиотеката да игнорира DateTime масажирането. По този начин firestore ще обработва правилно това поле като клеймо за време. static DateTime _dateTime(DateTime dateTime) => dateTime; - person mike123; 30.03.2020
comment
Как дефинирахте въведеното поле за дата/час в модела на dart? Любезно предложете. Благодаря. - person Kamlesh; 15.06.2021
comment
Timestamp е тип от библиотеката cloud_firestore - person Reed; 17.06.2021