Search code examples
androidjsonflutterdartflutter-dependencies

Json_Annotation does not handle nested classes properly


I have nested classes InventoryResult and Item that I want to read/write with json files, and the generated code by json_annotation does not map Item list in the toJson() method; curiously handling is totally fine for fromJson() method. Below are my 2 classes:

@JsonSerializable()
class InventoryResult {
  List<Item> results;
  int number;
  int offset;
  int totalResults;

  InventoryResult({required this.results, required this.number, required this.offset, required this.totalResults});
  factory InventoryResult.fromJson(Map<String, dynamic> json) => _$InventoryResultFromJson(json);
  Map<String, dynamic> toJson() => _$InventoryResultToJson(this);

}

@JsonSerializable()
class Item {
  int id;
  String productName;

  Item(
      {required this.id,
      required this.productName});

  factory Item.fromJson(Map<String, dynamic> json) => _$ItemFromJson(json);
  Map<String, dynamic> toJson() => _$ItemToJson(this);
}

and Inventory's generated fromJson() and toJson() methods differ oddly:

InventoryResult _$InventoryResultFromJson(Map<String, dynamic> json) =>
    InventoryResult(
      results: (json['results'] as List<dynamic>)
          .map((e) => Item.fromJson(e as Map<String, dynamic>))
          .toList(),
      number: (json['number'] as num).toInt(),
      offset: (json['offset'] as num).toInt(),
      totalResults: (json['totalResults'] as num).toInt(),
    );

Map<String, dynamic> _$InventoryResultToJson(InventoryResult instance) =>
    <String, dynamic>{
      'results': instance.results, //INCORRECT NESTED CLASS HANDLING HERE
      'number': instance.number,
      'offset': instance.offset,
      'totalResults': instance.totalResults,
    };

Naturally as a result, when I read from Json everything is correct, when I write to json it just writes as 'Instance' instead of correctly parsing Item object. I have json_annotation and json_serializable also up to date and totally stumped. Can it be related to that I added Item's toJson() method later in the project? I have re-built many times after that, also tried deleting generated g.dart files altogether as well. Both class files import each other and json_annotation package, and the g.dart files. Item's generated g.dart file appears correct with fromJson() and toJson() methods. I cannot find the issue.


Solution

  • Add the explicitToJson: true flag to the @JsonSerializable annotation of the outer class InventoryResult:

    @JsonSerializable(explicitToJson: true)
    class InventoryResult {
      // Your existing class fields and methods
    }
    

    This ensures that the generated toJson method will explicitly call toJson on nested objects like Item.

    Now, the generated _$InventoryResultToJson method will look like this:

    Map<String, dynamic> _$InventoryResultToJson(InventoryResult instance) =>
        <String, dynamic>{
          'results': instance.results.map((e) => e.toJson()).toList(), // Correctly serializes each Item
          'number': instance.number,
          'offset': instance.offset,
          'totalResults': instance.totalResults,
        };