1

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?

5
  • Your statistics value in the class StatisticsDto should return List<StatisticsDetailDto> statistics; and if your values names then not match JSON key you need to add manually @JsonKey(name: 'key). I recommend this extension which can convert JSON to json_serializable data marketplace.visualstudio.com/… Commented Apr 7, 2022 at 6:26
  • Thank you will try that asap. One question, why should it be List<StatisticsDetailDto>? I reckon that would apply to responses that return a list like {[...]} but in this case the response is a map {"USD": [], "EUR": []} etc etc Commented Apr 7, 2022 at 7:36
  • Because you return StatisticsDetailDto as List. Returns Map matches the StatisticsDetailDto data. Commented Apr 7, 2022 at 7:48
  • Just tried your changes, it still returns null unfortunately. The property 'statistics' from StatisticsDto is supposed to return a map of <String, List<StatisticsDetailDto>>, each String key has a value which is a List<StatisticsDetailDto>. I have been scavanging SO and the documentation for hours but could not find anything about this particular use case Commented Apr 7, 2022 at 7:56
  • Added a example. Commented Apr 7, 2022 at 9:26

2 Answers 2

2

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
        }
    }
}
Sign up to request clarification or add additional context in comments.

3 Comments

Thank you very much, this indeed works with a fixed list of currency codes. However, is there any way to achieve the same with a dynamic response e.g. { "USD": [], "EUR": [], "AUD": [], ... }?
I don't know where you from taking this JSON file but your main Map statistics have no key and that's why is always null, and you can't return it as Map. Map<[Key not exist in the JSON file], List<StatisticsDetailDto>> statistics
Explained in the my example.
0

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'],
      );
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.