rrousselGit / freezed

Code generation for immutable classes that has a simple syntax/API without compromising on the features.
https://pub.dev/packages/freezed
1.93k stars 237 forks source link

Supporting List of non primitive types in toJson #42

Closed Igos19 closed 4 years ago

Igos19 commented 4 years ago

Hi,

There's a problem when using property List of generated items, toJson method works incorrect.

@immutable
abstract class VideoModel with _$VideoModel {
  const factory VideoModel({@JsonKey(name: 'id') String id,
    @JsonKey(name: 'iso_639_1') String iso639,
    @JsonKey(name: 'iso_3166_1') String iso3166,
    @JsonKey(name: 'key') String key,
    @JsonKey(name: 'name') String name,
    @JsonKey(name: 'site') String site,
    @JsonKey(name: 'size') int size,
    @JsonKey(name: 'type') String type}) = _VideoModel;

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

@immutable
abstract class DataVideosModel with _$DataVideosModel {
  const factory DataVideosModel(
      {@JsonKey(name: 'results') List<VideoModel> results,
        @JsonKey(name: 'id') int id}) = _DataVideosModel;

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

Currently toJson method is generated like:

Map<String, dynamic> _$_$_DataVideosModelToJson(_$_DataVideosModel instance) =>
    <String, dynamic>{
      'results': instance.results,
      'id': instance.id,
    };

Could you make supporting List in toJson method like this: (in that cases when List isn't List of primivite types)

Map<String, dynamic> _$_$_DataVideosModelToJson(_$_DataVideosModel instance) =>
    <String, dynamic>{
      'results': instance.results.map((result) => result.toJson()),
      'id': instance.id,
    };
rrousselGit commented 4 years ago

Hi! That's the job of json_serializable, not freezed.

Freezed is merely delegating that part to json_serializable. Consider making an issue on their repository

knaeckeKami commented 4 years ago

Just as hint for anybody that also runs into this: this is supported by json_serializable, but has to be enabled, either per class or globally via the explicit_to_json flag in the build.yaml file.

Example:

@freezed
@JsonSerializable(explicitToJson: true)
abstract class MyModel with _$Model{ ... }
targets:
  $default:
    builders:
      json_serializable:json_serializable:
        options:
          explicit_to_json: true
Holofox commented 4 years ago

@knaeckeKami, when using @JsonSerializable annotation, duplicate methods are created:

user.dart:

@freezed
@JsonSerializable(explicitToJson: true)
abstract class User with _$User {
  factory User({
    String id,
    String displayName,
    UserLanguage language,
  }) = _User;

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

user.g.dart:

//This method is still created and used by Freezed
User _$UserFromJson(Map<String, dynamic> json) {
  return User();
}
//This method is created by @JsonSerializable
Map<String, dynamic> _$UserToJson(User instance) => <String, dynamic>{};

//This method is created by @JsonSerializable
_$_User _$_$_UserFromJson(Map<String, dynamic> json) {
  return _$_User(
    id: json['id'] as String,
    displayName: json['displayName'] as String,
    language: json['language'] == null
        ? null
        : UserLanguage.fromJson(json['language'] as Map<String, dynamic>),
  );
}
//This method is still created and used by Freezed
Map<String, dynamic> _$_$_UserToJson(_$_User instance) => <String, dynamic>{
      'id': instance.id,
      'displayName': instance.displayName,
      'language': instance.language,
    };

@rrousselGit, Is it possible to add the ability to change @JsonSerializable annotation properties through the freezed generator:

user.dart:

@freezed
abstract class User with _$User {
  factory User({
    String id,
    String displayName,
    UserLanguage language,
  }) = _User;

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

user.freezed.dart:

// Property passed to JsonSerializable annotation
@JsonSerializable(explicitToJson: true)
class _$_User implements _User {
  _$_User({this.id, this.displayName, this.language});

  factory _$_User.fromJson(Map<String, dynamic> json) =>
      _$_$_UserFromJson(json);

  @override
  final String id;
  @override
  final String displayName;
  @override
  final UserLanguage language;

  @override
  String toString() {
    return 'User(id: $id, displayName: $displayName, language: $language)';
  }

Do not judge strictly. I don’t know how annotations and generators interact with each other, but I tried to convey the problem.

rrousselGit commented 4 years ago

@Holofox what you want is likely #50

knaeckeKami commented 4 years ago

Ah yes, it seems that you need to use the approach via the build.yaml at the moment