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());
});
}
}