json_serializable.dart
json_serializable.dart copied to clipboard
Generics complicate self-referential class structure
The following code builds, but see the comment for which critical information has to be removed for said build to succeed:
class Base<T> {
const Base();
}
class BaseConverter<T> extends JsonConverter<Base, Map<String, Object?>> {
const BaseConverter();
@override
Map<String, Object?> toJson(Base object) => {};
@override
Base fromJson(Map<String, Object?> json) => Base();
}
@JsonSerializable()
class Data<T> extends Base<T> {
Data({
required this.data,
required this.name,
});
factory Data.fromJson(Map<String, dynamic> json) => _$DataFromJson(json);
final String name;
@BaseConverter()
// Ideally, this would be Base<T>, but defining it so breaks the build
final Base data;
Map<String, dynamic> toJson() => _$DataToJson(this);
}
Changing final Base data to final Base<T> data results in this error:
[SEVERE] json_serializable on lib/src/data.dart:
Could not generate `fromJson` code for `data`.
To support the type `Base` you can:
* Use `JsonConverter`
https://pub.dev/documentation/json_annotation/latest/json_annotation/JsonConverter-class.html
* Use `JsonKey` fields `fromJson` and `toJson`
https://pub.dev/documentation/json_annotation/latest/json_annotation/JsonKey/fromJson.html
https://pub.dev/documentation/json_annotation/latest/json_annotation/JsonKey/toJson.html
package:json_serializable_test/src/data.dart:30:17
╷
[line number] │ final Base<T> data;
This despite the fact that a JsonConverter is clearly being used.
For posterity, I originally reported this at rrousselGit/freezed#1074, and was (correctly, it seems) directed here.
I have same problem :c
class BaseConverter<T> extends JsonConverter<Base, Map<String, Object?>> {
What happens if you write Base<T> there, instead of Base?
Writing just Base with no arguments is equivalent, IIUC, to writing Base<dynamic>. Which doesn't seem like what you intend. And it makes sense that a converter that produces a Base<dynamic> wouldn't be seen as a match when the field is supposed to hold more specifically a Base<T>.
That said, I won't be surprised if that doesn't fix it. Here's a possibly related issue:
- #1398
which suggests that the logic for applying a JsonConverter doesn't currently do everything one would hope it would when there are type parameters involved.
@craiglabenz – is this what you want?
diff --git a/json_serializable/test/generic_files/for_craig.dart b/json_serializable/test/generic_files/for_craig.dart
index 5555d80..bb0011e 100644
--- a/json_serializable/test/generic_files/for_craig.dart
+++ b/json_serializable/test/generic_files/for_craig.dart
@@ -6,12 +6,12 @@ class Base<T> {
const Base();
}
-class BaseConverter<T> extends JsonConverter<Base, Map<String, Object?>> {
+class BaseConverter<T> extends JsonConverter<Base<T>, Map<String, Object?>> {
const BaseConverter();
@override
Map<String, Object?> toJson(Base object) => {};
@override
- Base fromJson(Map<String, Object?> json) => const Base();
+ Base<T> fromJson(Map<String, Object?> json) => Base<T>();
}
@JsonSerializable()
@@ -26,8 +26,7 @@ class Data<T> extends Base<T> {
final String name;
@BaseConverter()
- // Ideally, this would be Base<T>, but defining it so breaks the build
- final Base data;
+ final Base<T> data;
Map<String, dynamic> toJson() => _$DataToJson(this);
}
And instantiate BaseConverter with the right generic parameter?
Looks right, @kevmoo
I have encountered the same problem with generics types on class members. Is there a solution. For now I've created several converters for each <T>. For now I have four types, so it's still manageble, but I plan to have many others.
I tried to call static method Base<T> fromJson<T>(Map<String, dynamic> json) passed to JsonKey(fromJson: ...) but I get the same error as with generics converter.
I there a plan if and when de/serialization of generic class members will be implemented?
No plan, @edlman. This is very much my hobby project. Happy to fix critical issues in main use cases, but this is very specialized/complicated.
PRs welcome!