Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

JsonConverter for unions generated by freezed package in dart

I'm trying to implement toJson/fromJson for a union generated by the freezed package. Let's say we have a class as following:

@freezed
abstract class Result with _$Result {
  const factory Result.error(String message) = Error;
  const factory Result.success() = Success;

  factory Result.fromJson(Map<String, dynamic> json) => _$ResultFromJson(json);
}

Where I want to/fromJson to behave as following:

 Result error = Result.error('some error');
 expect(error.toJson(), {'type': 'error', 'message': 'some error'});
 expect(Result.fromJson({'type': 'error', 'message': 'some error'}), error);

It's stated in the docs that you can use a JsonConverter (fromJSON with multiple classes) but I don't know how to use it properly.

class ResultConverter implements JsonConverter<Result, Map<String, dynamic>> {
  const ResultConverter();

  @override
  Result fromJson(Map<String, dynamic> json) {
    if (json == null) {
      return null;
    }
    switch (json['type'] as String) {
      case 'success':
        return Success.fromJson(json);
      case 'error':
        return Error.fromJson(json);

      default:
        throw FallThroughError();
    }
  }

  @override
  Map<String, dynamic> toJson(Result object) => object.map(
        error: (e) => {'type': 'error', ...e.toJson()},
        success: (s) => {'type': 'success', ...s.toJson()},
      );
}

fromJson works fine if the factory method calls ResultConverter().fromJson(this) instead of the generated one, but that feels like a workaround and will not work on toJson.

Is it possible to annotate the Result class somehow so that the codegeneration will use the converter?

like image 756
gugge Avatar asked Jun 17 '20 20:06

gugge


1 Answers

Yes, this resource has helped me - link to achieve it.

Plus, it works for dedicated named constructor in case of freezed package.

Like this (please note no abstract keyword and private constructor added):

@freezed 
class Result with _$Result {

  const Result._();

  @ResultConverter()
  const factory Result.error(String message) = Error;

  @ResultConverter()
  const factory Result.success() = Success;

  factory Result.fromJson(Map<String, dynamic> json) => _$ResultFromJson(json);
}
like image 100
Michał Dobi Dobrzański Avatar answered Oct 19 '22 12:10

Michał Dobi Dobrzański