Flutter json_serializable: fromJson returns null

1.7k views Asked by At

the json I receive from the api looks like this:

{
  "USD": [
    {
      "name": "something",
      "value": 123
    },
    {
      "name": "something else",
      "value": 1234
    }
  ],
  "EUR": [
    ... same here
  ]
}

and my dart model looks like:

@JsonSerializable(anyMap: true, includeIfNull: true)
class StatisticsDto {
  StatisticsDto({this.statistics});

  final Map<String, List<StatisticsDetailDto>> statistics;

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

  Map<String, dynamic> toJson() => _$StatisticsDtoToJson(this);
}

@JsonSerializable()
class StatisticsDetailDto {
  StatisticsDetailDto({
    this.name,
    this.value,
  });

  final String name;
  final num value;

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

  Map<String, dynamic> toJson() => _$StatisticsDetailDtoToJson(this);
}

I don't seem to be able to make it work, I get a 200 from the api, but the serialization returns null.

What am I doing wrong here?

2

There are 2 answers

3
Arnas On BEST ANSWER

This is what your JSON objects should look like converted to Dart data class with json_serializable

{
    "USD": [{
            "name": "something",
            "value": 123
        },
        {
            "name": "something else",
            "value": 1234
        }
    ],
    "EUR": [{
            "name": "something",
            "value": 123
        },
        {
            "name": "something else",
            "value": 1234
        }
    ]
}

Dart data class with json_serializable without Map support:

import 'package:json_annotation/json_annotation.dart';

part 'statistic_dto.g.dart';
part 'currency.g.dart';

@JsonSerializable()
class StatisticDto {
  StatisticDto({this.usd, this.eur});

  @JsonKey(name: 'USD')
  List<Currency>? usd;
  @JsonKey(name: 'EUR')
  List<Currency>? eur;

  factory StatisticDto.fromJson(Map<String, dynamic> json) {
    return _$StatisticDtoFromJson(json);
  }

  Map<String, dynamic> toJson() => _$StatisticDtoToJson(this);
}

@JsonSerializable()
class Currency {
  Currency({this.name, this.value});

  String? name;
  int? value;

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

  Map<String, dynamic> toJson() => _$CurrencyToJson(this);
}

Run:

flutter pub run build_runner build --delete-conflicting-outputs

In your first example, your Map statistics have no key that why it always returns null. JSON file would look like this for your first class Data

// For: final Map<String, Map<String, dynamic>> statistics;
{
    "statistics": {
        "USD": {
            "name": "One",
            "value": 1
        },
        "EUR": {
            "name": "Four",
            "value": 4
        }
    }
}
0
Fi Li Ppo On

If you are looking to serialize a json with fixed keys, please refer to Arnas' answer.

In my case, the json has dynamic keys, e.g. it's a map with an indefinite number of keys (corresponding to a currency code in my case), and a corresponding value (an array of objects).

Now, I reckon @JsonSerializable is not suitable for this kind of scenario, hence I implemented my own .fromJson() factories.

class StatisticsDto {
  StatisticsDto({this.statistics});

  final Map<String, List<StatisticsDetailDto>> statistics;

  factory StatisticsDto.fromJson(Map<String, dynamic> json) =>
      StatisticsDto(
          statistics: json.map((String currency, dynamic details) {
              final List<StatisticsDetailDto> currencyDetails = details
                .map<StatisticsDetailDto>(
                    (detail) => StatisticsDetailDto.fromJson(detail))
                .toList();
            return MapEntry<String, List<StatisticsDetailDto>>(currency, currencyDetails);
          })
      );
}

class StatisticsDetailDto {
  StatisticsDetailDto({
    this.name,
    this.value,
  });

  final String name;
  final num value;

  factory StatisticsDetailDto.fromJson(Map<String, dynamic> json) =>
      StatisticsDetailDto(
          type: json['name'],
          value: json['value'],
      );
}