skip to Main Content

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 ?

5

Answers


  1. Chosen as BEST ANSWER

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

  2. 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>;
    }
    
    Login or Signup to reply.
  3. 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)),
        );
    }
    
    Login or Signup to reply.
  4. 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.

    Login or Signup to reply.
  5. 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;
      }
    }
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search