I'm trying to use the dart json_serializable
package to encode/decode a custom type I'm writing/reading with Google Cloud Firestore, and I'm having an issue with the custom type having properties that are also custom types.
I'm storing a Habit which has two custom properties:
HabitFrequency
is a combination of an integer, and an enum frequencyType that I want to store together as a map in Firestore.
HabitCompletion
is a list of the type HabitCompletion
, which contains a date, completion status, and an optional string value/numeric value. I want this to be stored as an array of maps.
Whenever I try to save, I get this error:
[VERBOSE-2:dart_vm_initializer.cc(41)] Unhandled Exception: Invalid argument: Instance of 'HabitFrequency'
I have the top-level class and both custom classes used as properties (HabitFrequency
and HabitCompletion
) listed as JsonSerializable, and they have the appropriate fromJson
/toJson
methods specified.
Is there a different way I need to set these up before I run build_runner build to generate the right code to serialize these properly?
Here is my top-level class:
@JsonSerializable()
class Habit {
String? id;
String title;
@JsonKey(name: 'frequency')
HabitFrequency frequency;
List<HabitCompletion>? completions;
Habit({
this.id,
required this.title,
required this.frequency,
this.completions,
});
/// Connect the generated [_$PersonFromJson] function to the `fromJson`
/// factory.
factory Habit.fromJson(Map<String, dynamic> json) => _$HabitFromJson(json);
/// Connect the generated [_$PersonToJson] function to the `toJson` method.
Map<String, dynamic> toJson() => _$HabitToJson(this);
}
}
The Habit Frequency:
@JsonSerializable()
class HabitFrequency {
int amount;
HabitFrequencyType frequencyType;
HabitFrequency(this.amount, this.frequencyType);
/// Connect the generated [_$HabitFrequencyFromJson] function to the `fromJson`
/// factory.
factory HabitFrequency.fromJson(Map<String, dynamic> json) =>
_$HabitFrequencyFromJson(json);
/// Connect the generated [_$HabitFrequencyToJson] function to the `toJson` method.
Map<String, dynamic> toJson() => _$HabitFrequencyToJson(this);
}
And the Habit completion:
@JsonSerializable()
class HabitCompletion {
DateTime timestamp;
bool completion;
String? stringValue;
double? numericValue;
HabitCompletion(
{required this.timestamp,
required this.completion,
this.stringValue,
this.numericValue});
/// Connect the generated [_$PersonFromJson] function to the `fromJson`
/// factory.
factory HabitCompletion.fromJson(Map<String, dynamic> json) =>
_$HabitCompletionFromJson(json);
/// Connect the generated [_$PersonToJson] function to the `toJson` method.
Map<String, dynamic> toJson() => _$HabitCompletionToJson(this);
}
They each have one property that is just a simple enumeration.
If you want to see more of the code, here's a gist containing the file for this type (and the custom types used for the properties in questions), as well as the auto-generated code coming from json_serializable.
this can be resolved by setting explicitToJson
on the JsonSerializable annotation for the Habit class as mentioned in your gist
@JsonSerializable(explicitToJson: true)
class Habit{
....
}