Open lifeapps-42 opened 11 months ago
Maybe? How does Freezed do this?
FromJson methods for subtypes are generated as usual:
FirstSealedClassSubtype _$firstSealedClassSubtypeFromJson(Map<String, dynamic> json) =>
FirstSealedClassSubtype(
someAtribute: json['someAtribute'] as String,
);
SecondSealedClassSubtype _$secondSealedClassSubtypeFromJson(Map<String, dynamic> json) =>
SecondSealedClassSubtype(
someOtherAtribute: json['someOtherAtribute'] as int,
);
ToJson methods just need one additional key member called 'subtype'
in our case:
Map<String, dynamic> _$firstSealedClassSubtypeToJson(FirstSealedClassSubtype instance) =>
<String, dynamic>{
'someAtribute': instance.someAtribute,
'subtype': 'first', // the value is specified by user inside @JsonValue('first') (see my first comment)
};
Map<String, dynamic> _$secondSealedClassSubtypeToJson(SecondSealedClassSubtype instance) =>
<String, dynamic>{
'someOtherAtribute': instance.someOtherAtribute,
'subtype': 'second',
};
Plus we do need root fromJson implementation. That could be just redirecting map/switch:
RootSealedClass _$rootSealedClassFromJson(Map<String, dynamic> json) =>
switch (json['subtype']) {
'first' => _$firstSealedClassSubtypeFromJson(json),
'second' => _$secondSealedClassSubtypeFromJson(json),
_ => throw ArgumentError() // or some fallback?
};
The root toJson method should be abstract (so I editted my first comment)
I'd take a PR here – It's hard to judge if the complexity is worth it without seeing the PR – which makes me nervous about saying you should make the PR, though. 😄
I'd take a PR here – It's hard to judge if the complexity is worth it without seeing the PR – which makes me nervous about saying you should make the PR, though. 😄
I have precisely 0 exp with code generation)) But I'll try
This would be a nice addition.
Currently, we can implement it by manually marking the sealed
class subtypes with @JsonSerializable
, but if we want to have a fromJson
in the sealed
class we have to implement it manually, which is a little burdensome and error-prone...
The workaround described by @mateusfccp has the limitation that another @JsonSerializable
class that has the sealed
class as one of its properties can not be serialized.
sealed class Root {
factory Root.fromJson(Map<String, dynamic> json) {
final discriminator = json['type'] as String;
switch (discriminator) {
case 'child1':
return Child1.fromJson(json);
case 'child2':
return Child2.fromJson(json);
default:
throw Exception('Unknown class: $discriminator');
}
}
static Map<String, dynamic> toJson(Root obj) {
switch (obj.runtimeType) {
case Child1:
final data = (obj as Child1).toJson();
data['type'] = "child1";
return data;
case Child2:
final data = (obj as Child1).toJson();
data['type'] = "child2";
return data;
default:
throw Exception('Unknown class: $obj');
}
}
}
@JsonSerializable(explicitToJson: true)
class Child1 extends Root {
const Child1();
factory Child1.fromJson(Map<String, dynamic> json) => _$Child1FromJson(json);
Map<String, dynamic> toJson() => _$Child1ToJson(this);
}
@JsonSerializable(explicitToJson: true)
class Child2 extends Root {
const Child2);
factory Child2.fromJson(Map<String, dynamic> json) => _$Child2FromJson(json);
Map<String, dynamic> toJson() => _$Child2ToJson(this);
}
@JsonSerializable(explicitToJson: true)
class MyDataClass {
final data: Root; // <--- can not be serialized
MyDataClass(this.data);
factory MyDataClass.fromJson(Map<String, dynamic> json) => _$MyDataClassFromJson(json);
Map<String, dynamic> toJson() => _$MyDataClassToJson(this);
}
Thank you for this package!
I failed to find any discussions about this and it feels like essential feture at this moment.
Do you have any plan to implement union-like sealed class serialization, so we can avoid using freezed?
We could use this feature something like this (inspired by freezed):