Error while trying to parse json data using json_serializable package flutter

44 Views Asked by At

I am trying to parse local json data using json_serializable package. I just want to understand did I made any mistake in while generating classes using json_serializable or any thing. The error is

[VERBOSE-2:dart_vm_initializer.cc(41)] Unhandled Exception: type 'List' is not a subtype of type 'Map<String, dynamic>' #0 _MyHomePageState.loadRecipe (package:json_serializable_example/main.dart:64:46)

Here is my code

class _MyHomePageState extends State<MyHomePage> {

  @override
  void initState() {
    loadRecipe();
    super.initState();
  }
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        backgroundColor: Theme.of(context).colorScheme.inversePrimary,
        title: Text(widget.title),
      ),
      body: Center(
        child: Container(),
      ),
    );
  }

  Future<String> _loadARecipeAsset() async {
    await Future.delayed(const Duration(seconds: 1));
    return await rootBundle.loadString('assets/recipe.json');
  }

  Future<RecipeList> loadRecipe() async {
    String response = await _loadARecipeAsset();
    final jsonParsedResponse = json.decode(response);
    RecipeList recipes = RecipeList.fromJson(jsonParsedResponse);
    return recipes;
  }
}

recipe.json

[
  {
    "id": "0001",
    "type": "donut",
    "name": "Cake",
    "ppu": 0.55,
    "batters":
    {
      "batter":
      [
        { "id": "1001", "type": "Regular" },
        { "id": "1002", "type": "Chocolate" },
        { "id": "1003", "type": "Blueberry" },
        { "id": "1004", "type": "Devil's Food" }
      ]
    },
    "topping":
    [
      { "id": "5001", "type": "None" },
      { "id": "5002", "type": "Glazed" },
      { "id": "5005", "type": "Sugar" },
      { "id": "5007", "type": "Powdered Sugar" },
      { "id": "5006", "type": "Chocolate with Sprinkles" },
      { "id": "5003", "type": "Chocolate" },
      { "id": "5004", "type": "Maple" }
    ]
  },
  {
    "id": "0002",
    "type": "donut",
    "name": "Raised",
    "ppu": 0.55,
    "batters":
    {
      "batter":
      [
        { "id": "1001", "type": "Regular" }
      ]
    },
    "topping":
    [
      { "id": "5001", "type": "None" },
      { "id": "5002", "type": "Glazed" },
      { "id": "5005", "type": "Sugar" },
      { "id": "5003", "type": "Chocolate" },
      { "id": "5004", "type": "Maple" }
    ]
  },
  {
    "id": "0003",
    "type": "donut",
    "name": "Old Fashioned",
    "ppu": 0.55,
    "batters":
    {
      "batter":
      [
        { "id": "1001", "type": "Regular" },
        { "id": "1002", "type": "Chocolate" }
      ]
    },
    "topping":
    [
      { "id": "5001", "type": "None" },
      { "id": "5002", "type": "Glazed" },
      { "id": "5003", "type": "Chocolate" },
      { "id": "5004", "type": "Maple" }
    ]
  }
]

Models

recipelist_response.dart

import 'package:json_annotation/json_annotation.dart';
import 'package:json_serializable_example/models/recipe_response.dart';
part 'recipelist_response.g.dart';

@JsonSerializable(explicitToJson: true)
class RecipeList{
  List<Recipe> recipe;

  RecipeList({
    required this.recipe,
});

  factory RecipeList.fromJson(Map<String, dynamic> json) => _$RecipeListFromJson(json);

  Map<String, dynamic> toJson() => _$RecipeListToJson(this);
}

recipelist_response.g.dart

// GENERATED CODE - DO NOT MODIFY BY HAND

part of 'recipelist_response.dart';

// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************

RecipeList _$RecipeListFromJson(Map<String, dynamic> json) => RecipeList(
      recipe: (json['recipe'] as List<dynamic>)
          .map((e) => Recipe.fromJson(e as Map<String, dynamic>))
          .toList(),
    );

Map<String, dynamic> _$RecipeListToJson(RecipeList instance) =>
    <String, dynamic>{
      'recipe': instance.recipe.map((e) => e.toJson()).toList(),
    };

recipe_response.dart

import 'package:json_annotation/json_annotation.dart';
import 'package:json_serializable_example/models/batters.dart';
import 'package:json_serializable_example/models/topping.dart';

part 'recipe_response.g.dart';

@JsonSerializable(explicitToJson: true)
class Recipe {
  String id;
  String type;
  String name;
  double ppu;
  Batters batters;
  List<Topping> topping;

  Recipe({
    required this.id,
    required this.type,
    required this.name,
    required this.ppu,
    required this.batters,
    required this.topping,
  });

  factory Recipe.fromJson(Map<String, dynamic> json) => _$RecipeFromJson(json);

  Map<String, dynamic> toJson() => _$RecipeToJson(this);
}

recipe_response.g.dart

// GENERATED CODE - DO NOT MODIFY BY HAND

part of 'recipe_response.dart';

// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************

Recipe _$RecipeFromJson(Map<String, dynamic> json) => Recipe(
      id: json['id'] as String,
      type: json['type'] as String,
      name: json['name'] as String,
      ppu: (json['ppu'] as num).toDouble(),
      batters: Batters.fromJson(json['batters'] as Map<String, dynamic>),
      topping: (json['topping'] as List<dynamic>)
          .map((e) => Topping.fromJson(e as Map<String, dynamic>))
          .toList(),
    );

Map<String, dynamic> _$RecipeToJson(Recipe instance) => <String, dynamic>{
      'id': instance.id,
      'type': instance.type,
      'name': instance.name,
      'ppu': instance.ppu,
      'batters': instance.batters.toJson(),
      'topping': instance.topping.map((e) => e.toJson()).toList(),
    };

batters.dart

import 'package:json_annotation/json_annotation.dart';
import 'package:json_serializable_example/models/topping.dart';

part 'batters.g.dart';

@JsonSerializable(explicitToJson: true)
class Batters {
  List<Topping> batter;

  Batters({
    required this.batter,
  });

  factory Batters.fromJson(Map<String, dynamic> json) => _$BattersFromJson(json);

  Map<String, dynamic> toJson() => _$BattersToJson(this);
}

batters.g.dart

// GENERATED CODE - DO NOT MODIFY BY HAND

part of 'batters.dart';

// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************

Batters _$BattersFromJson(Map<String, dynamic> json) => Batters(
      batter: (json['batter'] as List<dynamic>)
          .map((e) => Topping.fromJson(e as Map<String, dynamic>))
          .toList(),
    );

Map<String, dynamic> _$BattersToJson(Batters instance) => <String, dynamic>{
      'batter': instance.batter.map((e) => e.toJson()).toList(),
    };

topping.dart

import 'package:json_annotation/json_annotation.dart';

part 'topping.g.dart';

@JsonSerializable()
class Topping {
  String id;
  String type;

  Topping({
    required this.id,
    required this.type,
  });

  factory Topping.fromJson(Map<String, dynamic> json) => _$ToppingFromJson(json);

  Map<String, dynamic> toJson() => _$ToppingToJson(this);
}

topping.g.dart

// GENERATED CODE - DO NOT MODIFY BY HAND

part of 'topping.dart';

// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************

Topping _$ToppingFromJson(Map<String, dynamic> json) => Topping(
      id: json['id'] as String,
      type: json['type'] as String,
    );

Map<String, dynamic> _$ToppingToJson(Topping instance) => <String, dynamic>{
      'id': instance.id,
      'type': instance.type,
    };
1

There are 1 best solutions below

2
On

RecipeList.fromJson would expect your JSON to be something like this:

{
  "recipe": 
  [
    {
      "id": "0001",
      "type": "donut",
      "name": "Cake",
      "ppu": 0.55,
      "batters": {
        // ...
      },
      "topping": [
        // ...
      ]
    },
    // ...
  ]
}

So the quickest fix you can do is to change from this:

RecipeList.fromJson(jsonParsedResponse)

to this:

RecipeList.fromJson({"recipe": jsonParsedResponse})

You can also customize the fromJson method as shown in the package's readme.