Home > database >  How to properly create instances containing a Map from a json file in dart?
How to properly create instances containing a Map from a json file in dart?

Time:02-04

I'm trying to deserialize a json file and create instances from it but whatever way I use, I end up stucked because of the dynamic type :

type '_Map<String, dynamic>' is not a subtype of type 'Map<String, int>'

Here's my model :

class Race {
  final String name;
  final Map<String, int> abilitiesUpdater;

  const Race({
    required this.name,
    required this.abilitiesUpdater
  });

  static fromJson(json) => Race(name: json['name'], abilitiesUpdater: json['abilitiesUpdater']);
}

Here's how I'm trying to deserialize the json file :

class RacesApi {
  static Future<List<Race>> getRacesLocally(BuildContext context) async {
    final assetBundle = DefaultAssetBundle.of(context);
    final String fileContent = await assetBundle.loadString('Assets/COC_Monstres/Races.json');

    List<dynamic> parsedListJson = jsonDecode(fileContent);
    List<Race> racesList = List<Race>.from(parsedListJson.map<Race>((dynamic i) => Race.fromJson(i)));
    return racesList;
  }
}

Here's my json file :

[
  {
    "name": "Vampire",
    "abilitiesUpdater": {
      "DEX": 2,
      "CHA": 2
    }
  },
  {
    "name": "Créature du lagon",
    "abilitiesUpdater": {
      "FOR": 2,
      "CON": 2
    }
  },
  ...
]

How can I properly cast this json object to fit into my class ?

CodePudding user response:

This works:

class Race {
  final String name;
  // changed to dynamic
  final Map<String, dynamic> abilitiesUpdater;

  const Race({required this.name, required this.abilitiesUpdater});

  static fromJson(json) =>
      Race(name: json['name'], abilitiesUpdater: json['abilitiesUpdater']);
}

Maybe after get the object you can parse that dynamic into int if you need it.

CodePudding user response:

Change your Model Class to this:

class Race {
  Race({
    required this.name,
    required this.abilitiesUpdater,
  });
  late final String name;
  late final AbilitiesUpdater abilitiesUpdater;
  
  Race.fromJson(Map<String, dynamic> json){
    name = json['name'];
    abilitiesUpdater = AbilitiesUpdater.fromJson(json['abilitiesUpdater']);
  }

  Map<String, dynamic> toJson() {
    final _data = <String, dynamic>{};
    _data['name'] = name;
    _data['abilitiesUpdater'] = abilitiesUpdater.toJson();
    return _data;
  }
}

class AbilitiesUpdater {
  AbilitiesUpdater({
    required this.FOR,
    required this.CON,
  });
  late final int FOR;
  late final int CON;
  
  AbilitiesUpdater.fromJson(Map<String, dynamic> json){
    FOR = json['FOR'];
    CON = json['CON'];
  }

  Map<String, dynamic> toJson() {
    final _data = <String, dynamic>{};
    _data['FOR'] = FOR;
    _data['CON'] = CON;
    return _data;
  }
}

CodePudding user response:

you can cast the json['abilitiesUpdater'] as Map<String, int> because internally flutter will set it default as Map<String, dynamic>

Code

class Race {
  final String name;
  final Map<String, int> abilitiesUpdater;

  const Race({
    required this.name,
    required this.abilitiesUpdater
  });

  static fromJson(json) => Race(name: json['name'], abilitiesUpdater: json['abilitiesUpdater']) as Map<String,int>;
}

CodePudding user response:

it is working fine with me i tried it here is the link to the code https://dartpad.dev/?id=550918b56987552eb3d631ce8cb9e063.

If you still getting error you can try this

class Race {
  final String name;
  final Map<String, int> abilitiesUpdater;

   Race({
     required this.name,
     required this.abilitiesUpdater
  });

  static fromJson(json) => Race(name: json['name'], abilitiesUpdater: (json['abilitiesUpdater']as Map<String,int>)) ;
 }

or you can try this

  class Race {
final String name;
final Map<String, int> abilitiesUpdater;

const Race({required this.name, required this.abilitiesUpdater});

static fromJson(json) => Race(
      name: json['name'],
      abilitiesUpdater: json['abilitiesUpdater']
          .map((key, value) => MapEntry<String, int>(key, value as int)),
    );
}

CodePudding user response:

Edit : To have something a little bit more handy and scalable, I created an extension, and it works fine eventhough I have to cast twice the object...

My model :

// import my extension

class Race {
  Race({
    required this.name,
    required this.abilitiesUpdater,
  });
  late final String name;
  late final Map<String, int> abilitiesUpdater;
  // late final AbilitiesUpdater abilitiesUpdater;

  Race.fromJson(Map<String, dynamic> json){
    name = json['name'];
    abilitiesUpdater = (json['abilitiesUpdater'] as Map<String, dynamic>).parseToStringInt();
  }
}

My extension :

extension Casting on Map<String, dynamic> {
  Map<String, int> parseToStringInt() {
    final Map<String, int> map = {};

    forEach((key, value) {
      int? testInt = int.tryParse(value.toString());
      if (testInt != null) {
        map[key] = testInt;
      } else {
        debugPrint("$value can't be parsed to int");
      }
    });
    return map;
  }
}

Once again, any help on cleaning this is appreciated !


Original answer :

Thanks to Sanket Patel's answer, I ended up with a few changes that made my code works. However I'm pretty clueless on why I can't directly cast a

Map<String, dynamic>

object into a

Map<String, int>

one. Any info on this would be appreciated :)

Here's how I changed my model class in the end :

class Race {
  Race({
    required this.name,
    required this.abilitiesUpdater,
  });
  late final String name;
  late final AbilitiesUpdater abilitiesUpdater;

  Race.fromJson(Map<String, dynamic> json){
    name = json['name'];
    abilitiesUpdater = AbilitiesUpdater.fromJson(json['abilitiesUpdater']);
  }
}

class AbilitiesUpdater {

  final Map<String, int> abilitiesUpdater = {};

  AbilitiesUpdater.fromJson(Map<String, dynamic> json){
    json.forEach((key, value) {
      abilitiesUpdater[key] = int.parse(value.toString());
    });
  }
}
  • Related