google / google/json_serializable.dart
[bug] json_serializable is unable to recognize generics on `@JsonKey`'s `toJson/fromJson` methods, or on `JsonConverter`
- Dominant language
- Dart
- Stars
- 1.6k
- Forks
- 461
- Avg merge
- 45m
- Merged PRs (30d)
- 1
Description
TL;DR: minimal reproducible bug:
```dart
class A {}
A fromJson(String input) => A();
String toJson(A input) => '$input';
@JsonSerializable()
class Serializable {
const Serializable({required this.a});
@JsonKey(fromJson: fromJson, toJson: toJson)
final A a;
}
```
The above breaks the builder.
---
In an attempt to work around #1507, I end up with the following:
```dart
sealed class Slug {}
class Asd extends Slug {}
class Lol extends Slug {}
class Rofl extends Slug {}
class Lmao extends Slug<(int, int)> {}
@JsonSerializable()
class Serializable {
const Serializable(this.slug);
final Slug slug;
}
```
The above outputs:
```sh
[SEVERE] json_serializable on lib/src/features/models/lol_dto.dart:
Could not generate `fromJson` code for `slug`.
To support the type `Slug` 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:asdlol/src/features/models/lol_dto.dart:19:17
╷
19 │ final Slug slug;
│ ^^^^
╵
```
**But that's expected**.
So, I follow the first advice, the `JsonConverter` way.
```dart
@JsonSerializable()
class Serializable {
const Serializable(this.slug);
@SlugConverter() // this has been added
final Slug slug;
}
class SlugConverter extends JsonConverter, String> {
const SlugConverter();
@override
String toJson(Slug object) {
return object.runtimeType.toString();
}
@override
Slug fromJson(String json) {
switch (json) {
case 'Asd':
return Asd() as Slug;
case 'Lol':
return Lol() as Slug;
case 'Rofl':
return Rofl() as Slug;
case 'Lmao':
return Lmao() as Slug;
default:
throw Exception('Unknown slug type: $json');
}
}
}
```
But the above is straight ignored by the builder, which outputs exactly the same message.
Am I doing something wrong? If so, can I be welcomed with a more informative message? 😸
Then, I try the `JsonKey` way.
```dart
@JsonSerializable()
class Serializable {
const Serializable(this.slug);
@JsonKey(fromJson: slugFromJson, toJson: slugToJson) // let's try this one now
final Slug slug;
}
String slugToJson(Slug object) {
return object.runtimeType.toString();
}
Slug slugFromJson(String json) {
switch (json) {
case 'Asd':
return Asd() as Slug;
case 'Lol':
return Lol() as Slug;
case 'Rofl':
return Rofl() as Slug;
case 'Lmao':
return Lmao() as Slug;
default:
throw Exception('Unknown slug type: $json');
}
}
```
But the above leads the builder to output:
```sh
[SEVERE] json_serializable on lib/src/features/models/lol_dto.dart:
Error with `@JsonKey` on the `slug` field. The `toJson` function `slugToJson` argument type `Slug` is not compatible with field type `Slug`.
package:asdlol/src/features/models/lol_dto.dart:19:17
╷
19 │ final Slug slug;
│ ^^^^
╵
```
Say again? `Slug` is not compatible with `Slug`? 😵💫
I'm unsure what I can actually do at this point.
Contributor guide
Assessment
This issue has not been assessed yet.