google / json_serializable.dart

Generates utilities to aid in serializing to/from JSON.
https://pub.dev/packages/json_serializable
BSD 3-Clause "New" or "Revised" License
1.54k stars 392 forks source link

Use of a generic JsonConverter #1398

Open alexdess opened 4 months ago

alexdess commented 4 months ago

Hello,

I'm stuck on a use case, and am looking for help. I'm using a graphql api that I don't want to modify (directus) and in some cases, the list of elements (here posts) is nested in a sub-element (here "post_id", see fromJson in the WrappedConverter2).

@freezed
class ExamSessionFullDto with _$ExamSessionFullDto implements IExamSessionFull {
  const ExamSessionFullDto._(); 

  const factory ExamSessionFullDto({
    @StringToIntConverter() required int id,
    required String title,
    @WrappedConverter2() List<PostDto>? posts,
  }) = _ExamSessionFullDto;

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

class WrappedConverter2 extends JsonConverter<PostDto, Map<String, dynamic>> {
  const WrappedConverter2();
  @override
  PostDto fromJson(Map<String, dynamic> json) {
    return PostDto.fromJson(json["post_id"]);
  }

  @override
  Map<String, dynamic> toJson(PostDto object) {
    return object.toJson();
  }
}

This code works, but I have to have a converter every time I have a list of items, which isn't ideal because I have to do a lot of copy-and-paste code, which is bad practice.

I've made several attempts to make my converter generic, but unfortunately when I use genericity it doesn't work with freezed, or it's probably me who can't get it right.

Does anyone have a good way to have a single JsonConverter or some other solution that I can apply to all my objects that have sublists of this type?

The idea would be to have something like

class ManyToManyRelationshipConverter<E>
    extends JsonConverter<E, Map<String, dynamic>> {
  final String jsonKey;
  final KreaticItemsConverter<E> converter;

  const ManyToManyRelationshipConverter.forPost(
      {this.jsonKey = "post_id", this.converter = const PostDtoConverter() as KreaticItemsConverter<E>});

  @override
  E fromJson(Map<String, dynamic> json) {
    return converter.fromJson(json[jsonKey]);
  }

  @override
  Map<String, dynamic> toJson(E object) {
    return converter.toJson(object);
  }
}