json_serializable.dart icon indicating copy to clipboard operation
json_serializable.dart copied to clipboard

Please explain generic types deserialization

Open subzero911 opened this issue 3 years ago • 6 comments

Generated code:

Response<T> _$ResponseFromJson<T>(
  Map<String, dynamic> json,
  T Function(Object json) fromJsonT,
) {
  return Response<T>()
    ..status = json['status'] as int
    ..value = fromJsonT(json['value']);
}

Seems like it is not fully automated for any types. It claims to provide Function for concrete types parsing. My try:

@JsonSerializable(createToJson: false, genericArgumentFactories: true)
class Response<T> {
  final T data;
  final Status status;

  Response({this.data, this.status});

  factory Response.fromJson(Map<String, dynamic> json) => _$ResponseFromJson(json, (jsonObject) {
        switch (jsonObject) {
          case User:
            return User.fromJson(jsonObject) as T;
          case Profile:
            return Profile.fromJson(jsonObject) as T;
          case StartChangeClientPhoneResponseBody:
            return StartChangeClientPhoneResponseBody.fromJson(jsonObject) as T;
          // primitive types, bool, etc.
          default:
            return jsonObject;
        }
      });
}

It throws runtime exception: Dart Unhandled Exception: type '_InternalLinkedHashMap<String, dynamic>' is not a subtype of type 'Profile'

Documentation says nothing on how to handle it properly.

subzero911 avatar Dec 01 '20 18:12 subzero911

I don't think you can switch over instances + types.

kevmoo avatar Dec 01 '20 23:12 kevmoo

Maybe this example will help you? https://github.com/google/json_serializable.dart/blob/master/example/lib/tuple_example.dart

kevmoo avatar Dec 01 '20 23:12 kevmoo

Maybe this example will help you? https://github.com/google/json_serializable.dart/blob/master/example/lib/tuple_example.dart

Thanks, this signature is correct: factory Response.fromJson(Map<String, dynamic> json, T Function(Object json) fromJsonT) => _$ResponseFromJson(json, fromJsonT); The problem is that Retrofit doesn't generate fromJsonT of correct type. It generates final value = Response<User>.fromJson(_result.data);, omitting the 2nd parameter.

subzero911 avatar Dec 02 '20 04:12 subzero911

What's Retrofit?

kevmoo avatar Dec 02 '20 04:12 kevmoo

It is an extension for Dio HTTP client: https://pub.dev/packages/retrofit It is a popular API generator, which we use.

subzero911 avatar Dec 02 '20 04:12 subzero911

Okay, it is a problem on Retrofit side, and they are aware of it and probably fix it in a next release. Currently I can manually make requests like

final _result = await dio.get('$baseUrl/currentUser');
return Response<User>.fromJson(_result.data, (data) => User.fromJson(data));

It's worth mentioning in docs that factory for generic types is factory Response.fromJson(Map<String, dynamic> json, T Function(Object json) fromJsonT) => _$ResponseFromJson(json, fromJsonT); because now it is not, and it made me lost: image

subzero911 avatar Dec 02 '20 08:12 subzero911