What is the best way to unmarshall this json in dart:

{
"Ace-spades-2": {
    "rank": { "shortName": "A", "longName": "Ace" },
    "suit": { "name": "spades" },
    "id": "Ace-spades-2"
},
"Two-spades-2": {
    "rank": { "shortName": "2", "longName": "Two" },
    "suit": { "name": "spades" },
    "id": "Two-spades-2"
},
"Three-spades-2": {
    "rank": { "shortName": "3", "longName": "Three" },
    "suit": { "name": "spades" },
    "id": "Three-spades-2"
},
{....a bunch more cards}

}

Here are my classes that I want to unmarshall into. Specifically I want to make the data Map<String,Card>. How can I do this?

class Card {
  Rank rank;
  Suit suit;
  String id;
  Card({this.rank,this.suit,this.id})
}

class Rank {
  String shortName;
  String longName;
  Rank({this.shortName, this.longName});
}

class Suit {
  String name;
  Suit({this.name});
}

In Go, I would just do cards := map[string]Card{} json.Unmarshal(<some_json>, &cards).

What is the best way to do this in dart?


Solution 1: Mabsten

For serialization in Flutter (where reflection is not available) is very common to use the json_serializable package. A good tutorial about this package is also in the flutter doc: https://flutter.dev/docs/development/data-and-backend/json#serializing-json-using-code-generation-libraries

When you have annotated yours classes (and you have completed the others installation instructions - also remember the annotation on the library declaration, which is mandatory), you can take advantage of the Card.fromJson() factory constructor. You can convert your json raw String in a Map<String, dynamic> using the jsonDecode() function (of the dart:convert package), then iterate on the Map and convert every entry in the Map with Card.fromJson. For example:

Map<String, Card> cardMap = (jsonDecode(jsonString) as Map<String, dynamic>).map((key, cardString) => MapEntry(key, Card.fromJson(cardString)));

A very important note: when you have a class (Card and Rank, in your example) with fields whose type is not primitive, but instead represents another class annotated with JsonSerializable, the JsonSerializable annotation must pass explicitToJson: true (i.e. @JsonSerializable(explicitToJson: true) class Card).

The process to serialize class/deserialize json the first time isn't as fast as in Go, but flutter doesn't have reflection.