Freezed: Nested Freezed class not converted TO JSON

Created on 3 Mar 2020  ·  10Comments  ·  Source: rrousselGit/freezed

👋
Given two freezed classes where one is "nested" in the other.

import 'package:freezed_annotation/freezed_annotation.dart';

part 'contrived_example.g.dart';
part 'contrived_example.freezed.dart';

@freezed
abstract class TopLevel with _$TopLevel {
  const factory TopLevel(
    String niceString,
    Nested doesNotSerializeButDoesDeserialize,
  ) = _TopLevel;
  factory TopLevel.fromJson(Map<String, dynamic> json) =>
      _$TopLevelFromJson(json);
}

@freezed
abstract class Nested with _$Nested {
  const factory Nested(int niceInteger) = _Nested;
  factory Nested.fromJson(Map<String, dynamic> json) => _$NestedFromJson(json);
}

The generated code for json_serializable doesn't serialize the nested object. DEserialization works as expected.

// GENERATED CODE - DO NOT MODIFY BY HAND

part of 'contrived_example.dart';

// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************

_$_TopLevel _$_$_TopLevelFromJson(Map<String, dynamic> json) {
  return _$_TopLevel(
    json['niceString'] as String,
    json['doesNotSerializeButDoesDeserialize'] == null
        ? null
        : Nested.fromJson(
            json['doesNotSerializeButDoesDeserialize'] as Map<String, dynamic>),
  );
}

Map<String, dynamic> _$_$_TopLevelToJson(_$_TopLevel instance) =>
    <String, dynamic>{
      'niceString': instance.niceString,
      // 👇 should have toJson() or map((element) => element.toJson()) for Lists
      'doesNotSerializeButDoesDeserialize':
          instance.doesNotSerializeButDoesDeserialize,
    };

_$_Nested _$_$_NestedFromJson(Map<String, dynamic> json) {
  return _$_Nested(
    json['niceInteger'] as int,
  );
}

Map<String, dynamic> _$_$_NestedToJson(_$_Nested instance) => <String, dynamic>{
      'niceInteger': instance.niceInteger,
    };

At first, I thought that it's an issue with json_serializable but if that library is used by itself, it serializes nested objects marked with @JsonSerializable just fine.

Most helpful comment

OK, pardon me 🤦‍♂️ I'll leave the build.yaml set up here for any time travelers from the future.

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

Thank you!

All 10 comments

I have a very deeply nested object and it works fine for me.

The generated code you gave is generated by json_serializable, so I have no control over it.
Is freezed correctly adding @JsonSerializable annotation on Nested?
If it does, then it's unlikely to be a bug with freezed

The _$_Nested is annotated.

@JsonSerializable()
class _$_Nested implements _Nested {...}

Then it may be an issue with build_runner.

Try regenerating the sources (potentially deleting .dart_tool) and see if it works.

In any case, freezed does nothing besides adding this annotation. So if it's there, then I can't do anything 🙃

Whoops! Sorry to break it to you, Rémi, but the bug is _probably_ on your side 😅

I replicated the non-serializing behavior with regular classes too. All it took for the nested class to serialize was @JsonSerializable(explicitToJson: true) on the top-level class.

I mean it depends on what encoder you use.

The documentation of explicitToJson explicitly says that it's not needed using dart:convert.

If you need it, it's your job to add it or your encoder's job

OK, pardon me 🤦‍♂️ I'll leave the build.yaml set up here for any time travelers from the future.

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

Thank you!

thanks, resoDev and Remi, I am from the future and this conversation helped me

Works great!

But I was wondering if we can set explicitToJson to true for specific fields, and false for other fields.

I tried annotating each field but that didn't seem to work

Here's my use case:

@freezed
abstract class SearchFilters with _$SearchFilters {
  const factory SearchFilters({
     List<CategoryEnum> category,
     CustomClass customClass,
  }) = _SearchFilters;

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

I want customClass to be explicit to json, but the category to not be explicit to Json. Here's my CategoryEnum class:

@freezed
abstract class CategoryEnum implements _$CategoryEnum {
  const CategoryEnum._();
  const factory CategoryEnum(String label) = _CategoryEnum;
  static const category1 = CategoryEnum("Category 1");
  static const category2 = CategoryEnum("Category 2");

  factory CategoryEnum.fromJson(String val) {
    return CategoryEnum(val);
  }

  @override
  String toString() => this.label;
}

I don't think you can, no

Thanks for the quick response. I was able to fix this by setting explicitToJson to true, and using @JsonKey(toJson: CategoryEnum.serializeToList) to define a custom toJson function for my enum class

@freezed
abstract class CategoryEnum implements _$CategoryEnum {
  const CategoryEnum._();
  const factory CategoryEnum(String label) = _CategoryEnum;
  static const category1 = CategoryEnum("1");
  static const category2 = CategoryEnum("2");

  factory CategoryEnum.fromJson(String val) {
    return CategoryEnum(val);
  }

  static List<String> serializeToList(List<CategoryEnum> e) {
    return e.map((a) => a.toString()).toList();
  }

  @override
  String toString() => this.label;
}
Was this page helpful?
0 / 5 - 0 ratings