3

I'm using the freezed package to work with immutable models and make use of the built-in feature for json serialization by the json_serializable package. I have a simple User class/model with different union types (UserLoggedIn, UserGeneral, UserError):

@freezed
class User with _$User {
  const factory User(String id, String email, String displayName) =
      UserLoggedIn;
  const factory User.general(String email, String displayName) = UserGeneral;
  const factory User.error(String message) = UserError;

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

Since I'm using multiple constructors and don't want my API to include the runtimeType key as suggested by the documentation, I can write a converter (scroll a bit more down, sentence starts with: If you don't control the JSON response, then you can implement a custom converter.).

So based on that I wrote the following converter class:

class UserConverter implements JsonConverter<User, Map<String, dynamic>> {
  const UserConverter();

  @override
  User fromJson(Map<String, dynamic> json) {
    if (json['id'] != null) {
      return UserLoggedIn.fromJson(json);
    } else if (json['error'] != null) {
      return UserError.fromJson(json);
    } else {
      return UserGeneral.fromJson(json);
    }
  }

  @override
  Map<String, dynamic> toJson(User data) => data.toJson();
}

The documentation now references another class (a wrapper class) which would now use this converter via annotation, something like this:

@freezed
class UserModel with _$UserModel {
  const factory UserModel(@UserConverter() User user) = UserModelData;

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

Question: is it possible to make use of this converter without having to use a wrapper class (UserModel)?

Reasoning: this wrapper class is adding another layer of abstraction which is not needed (in my cases). Especially since the wrapper class does not have any other benefit / purpose and it feels like it should be possible to do that without using it.

kounex
  • 1,555
  • 4
  • 12
  • Does this https://stackoverflow.com/a/69709562/7071152 help you? You don't strictly need to use **_$UserFromJson** – Giovanni Londero Jan 25 '22 at 09:42
  • Thanks for the comment - unfortunately this does not work since the codegenerator for freezed only applies the from and to json functions for the union types (`UserLoggedIn`, `UserGeneral` etc.) with json_serializeable if you use `[...] => _$UserFromJson` – kounex Jan 25 '22 at 09:56

1 Answers1

5

This might sound obvious and lazy, but assuming the following suffice, why don't you write your custom fromJson in your model class?

// We write a custom fromjson but still want fromjson constructors
// to be generated
@Freezed(fromJson: true, toJson: true)
sealed class User with _$User {
  const factory User(String id, String email, String displayName) =
      UserLoggedIn;
  const factory User.general(String email, String displayName) = UserGeneral;
  const factory User.error(String message) = UserError;

  factory User.fromJson(Map<String, dynamic> json) {
    // We paste our JsonConverter.fromJson in a fromjson factory
    if (json['id'] != null) {
      return UserLoggedIn.fromJson(json);
    } else if (json['error'] != null) {
      return UserError.fromJson(json);
    } else {
      return UserGeneral.fromJson(json);
    }
  }
}

With this, you still obtain a toJson method, which is subject to other @freezed rules you've mentioned.

Rémi Rousselet
  • 256,336
  • 79
  • 519
  • 432
venir
  • 1,809
  • 7
  • 19