7

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?

gugge
  • 918
  • 1
  • 11
  • 17

3 Answers3

1

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);
}
Michał Dobi Dobrzański
  • 1,449
  • 1
  • 20
  • 19
0

change toJson(Result object) method to be like that

 @override
  Map<String, dynamic> toJson(Result object) => object.toJson(); 
Mohamed Kamel
  • 841
  • 10
  • 15
-2

You can just use your converter.

Try this:

final result = ResultConverter().fromJson(json);
Ajay Lingayat
  • 1,465
  • 1
  • 9
  • 25