Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

toJson/fromJson is broken for nested lists of freezed objects #232

Closed
vishna opened this issue Jul 15, 2020 · 4 comments
Closed

toJson/fromJson is broken for nested lists of freezed objects #232

vishna opened this issue Jul 15, 2020 · 4 comments

Comments

@vishna
Copy link

vishna commented Jul 15, 2020

Consider the following example:

import 'package:freezed_annotation/freezed_annotation.dart';

part 'main.freezed.dart';
part 'main.g.dart';

@freezed
abstract class TodoList with _$TodoList {
  factory TodoList(List<Todo> todos) = _TodoList;
  factory TodoList.fromJson(Map<String, dynamic> json) => _$TodoListFromJson(json);
}

@freezed
abstract class Todo with _$Todo {
  factory Todo(String name, bool checked) = _Todo;
  factory Todo.fromJson(Map<String, dynamic> json) => _$TodoFromJson(json);
}

Observe the following test fail:

import 'package:flutter_test/flutter_test.dart';

import 'package:freezed_json_bug/freezed_json_bug.dart';
import 'package:collection/collection.dart';

void main() {
  test('serialize deserialize', () {
    final todos = <Todo>[
      Todo("Item 1", true),
      Todo("Item 2", false),
    ];

    final todoList = TodoList(todos);

    final todoListJson = todoList.toJson();

    final todoListReserialized = TodoList.fromJson(todoListJson);

    Function deepEq = const DeepCollectionEquality().equals;

    expect(deepEq(todoList.toJson(), todoListReserialized.toJson()), true);
  });
}

with the following stacktrace:

type '_$_Todo' is not a subtype of type 'Map<String, dynamic>' in type cast
package:freezed_json_bug/src/main.g.dart 13:55        _$_$_TodoListFromJson.<fn>
dart:_internal                                        ListIterable.toList
package:freezed_json_bug/src/main.g.dart 14:11        _$_$_TodoListFromJson
package:freezed_json_bug/src/main.freezed.dart 89:7   new _$_TodoList.fromJson
package:freezed_json_bug/src/main.freezed.dart 12:20  _$TodoListFromJson
package:freezed_json_bug/src/main.dart 9:59           new TodoList.fromJson
test/freezed_json_bug_test.dart 17:43                 main.<fn>

This is because in main.g.dart we have:

Map<String, dynamic> _$_$_TodoListToJson(_$_TodoList instance) =>
    <String, dynamic>{
      'todos': instance.todos,
    };

rather than:

Map<String, dynamic> _$_$_TodoListToJson(_$_TodoList instance) =>
    <String, dynamic>{
      'todos': instance.todos.map((e) => e.toJson()).toList(),
    };
@rrousselGit
Copy link
Owner

This is the "expected" behavior of json_serializable

You are supposed to either specify a @JsonSerializable(explicitToJson: true) or change explicitToJson inside your build.yaml file. https://github.com/google/json_serializable.dart/tree/master/json_serializable#build-configuration

@vishna
Copy link
Author

vishna commented Jul 15, 2020

Thanks for the quick explainer, adding that annotation fixed the test:

@freezed
abstract class Todo with _$Todo {
  // must be above factory method
  @JsonSerializable(explicitToJson: true)
  factory Todo(String name, bool checked) = _Todo;
  factory Todo.fromJson(Map<String, dynamic> json) => _$TodoFromJson(json);
}

Optionally creating build.yaml mentioned in #86 (only stumbled upon this now 😅 ) is a fix too:

targets:
  $default:
    builders:
      json_serializable:
        options:
          explicit_to_json: true

Out of these two I think of going for build.yaml fix and making this explicitToJson true by default everywhere. It's less code to write and less things to worry about.

@Rajkarnikar-unish
Copy link

Is it just me or is there still anyone for whom these solutions are not working??
Please can anyone land here and help!!!

@y1lichen
Copy link

y1lichen commented Aug 22, 2023

@Rajkarnikar-unish

Is it just me or is there still anyone for whom these solutions are not working?? Please can anyone land here and help!!!

My solutions:

@freezed
class Parent with _$Parent {
  const factory Parent({
    @JsonKey(
        name: "child_key",
        fromJson: Parent._childFromJson)
    required List<Child> children,
  }) = _Parent;

  static List<Child> _childFromJson(
      List<dynamic> jsons) {
    List<Child> list = [];
    for (var json in jsons) {
      list.add(Child.fromJson(json));
    }
    return list;
  }

@freezed
class Child with _$Child {
  @JsonSerializable(explicitToJson: true)
  const factory Child({
    required String type,
    required String id,
  }) = _Child;

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants