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

How to deal with generic data?

Open hc79879 opened this issue 2 years ago • 4 comments

import 'package:json_annotation/json_annotation.dart'; part 'BaseResponse.g.dart';

@JsonSerializable() class BaseResponse<T> { int? errorCode; String? errorMsg; T? data;

BaseResponse({ this.errorCode, this.errorMsg, this.data, });

factory BaseResponse.fromJson(Map<String, dynamic> srcJson) => _$BaseResponseFromJson(srcJson);

Map<String, dynamic> toJson() => _$BaseResponseToJson(this); }

Could not generate fromJson code for data because of type T (type parameter).

How to use this generic data 'BaseResponse'?

hc79879 avatar Jul 29 '22 10:07 hc79879

@hc79879 U need to use converter for this.

Check code below for the answer.


@JsonSerializable()
class ApiResponse<T> {
  @_Converter()
  final T? result;
  final String resultCode;
  final String? resultMessage;

  ApiResponse({this.result, required this.resultCode, this.resultMessage});

  factory ApiResponse.fromJson(Map<String, dynamic> json) =>
      _$ApiResponseFromJson(json);
  Map<String, dynamic> toJson() => _$ApiResponseToJson(this);

  @override
  String toString() {
    return "$resultCode: $resultMessage";
  }
}

// generic list converter
class _Converter<T> implements JsonConverter<T?, Object?> {
  const _Converter();

  @override
  T fromJson(Object? json) {
    if (json == null) return null as T;

    if (typesEqual<T, String?>()) {
      return json as T;
    } else if (typesEqual<T, AuthToken?>()) {
      return AuthToken.fromJson(json as Map<String, dynamic>) as T;
    } 

    // note : currently dart does not support generic type checking without explicit definition of generic type.

    throw FormatException("No valid structure for json $json");
  }

  @override
  Object? toJson(T? object) {
    // This will only work if `object` is a native JSON type:
    //   num, String, bool, null, etc
    // Or if it has a `toJson()` function`.
    return object as Map<String, dynamic>?;
  }
}

wangmir avatar Aug 01 '22 14:08 wangmir

There's no need for converter, just enable genericArgumentFactories in JsonSerializable

@JsonSerializable(
  constructor: '_',
  createToJson: false,
  genericArgumentFactories: true,
)
class Page<T extends Object> {
  final int limit;
  final int total;
  final List<T> items;

  Page._(this.limit, this.total, this.items);

  factory Page.fromJson(Map<String, dynamic> json, T Function(Object? json) fromJsonT) => _$PageFromJson(json, fromJsonT);
}

and if I didn't forget anything, this should be enough. Of course your T must also be JsonSerializable enabled.

leoshusar avatar Aug 02 '22 16:08 leoshusar

how can I use this if I use?

@RestApi(parser: Parser.FlutterCompute)

meg4cyberc4t avatar Jan 04 '23 13:01 meg4cyberc4t

@trevorwang Please add generic support for Parser.FlutterCompute. It should accept optional additional argument T Function(Object? json) fromJsonT

romawizard avatar May 23 '23 09:05 romawizard