Search code examples
jsonflutterdartparsingjson-api-response-converter

How to handle different JSON Responses from same source in flutter


I have a flutter application retrieving a list/or anything for that matter at some point, and if there's any issue with the request, a different response is received.
For Example:

{
  "status" : "success",
  "message":
    [
      {
        "updated_on" : "2022-01-09 14:26:07"
      }
    ]
}

For Failure:

{
  "status" : "error",
  "message" : "Query not found"
}

Using quicktype.io, I have created class as below:

class ResponseList {
  ResponseList({
    required this.status,
    required this.message,
  });

  String status;
  List<dynamic> message;

  factory ResponseList.fromJson(Map<String, dynamic> json) => ResponseList(
        status: json["status"],
        message: List<dynamic>.from(json["message"].map((x) => CLASSNAME.fromJson(x))),
      );

  Map<String, dynamic> toJson() => {
        "status": status,
        "message": List<dynamic>.from(message.map((x) => x.toJson())),
      };
}

Now, the problem is, on failure, this raises an exception when i try to call ResponseList responseAllFromJson(String str) => ResponseList.fromJson(json.decode(str)); where the exeption says
[ERROR:flutter/lib/ui/ui_dart_state.cc(209)] Unhandled Exception: type 'String' is not a subtype of type 'List<dynamic>'.

I want to have a method that checks the status first, whether success or failure and then parses the remaining of the json (in message) or parses the whole json and returns appropriate response (text in case of failure and list of objects in case of success).
Note: I want a reusable code, so that I can pass the response to the method and retrieve it from any point in my application. For example:

static bool responseStatus(dynamic response) {
    if (response != null) {
      if (responseListFromJson(response).status.trim().toLowerCase() == 'success') {
        return true;
      }
    }
    return false;
  }

Presently, above raises exception on failure but works smoothly on success.


Solution

  • You can try this way while parsing.

    class MainResponse {
      String status;
      dynamic message;
    
      MainResponse({this.status, this.message});
    
      MainResponse.fromJson(Map<String, dynamic> json) {
        status = json['status'];
        if(json['message'] is String)  {
    
          message = json['message'];
        }else {
    
          message = <Message>[];
          json['message'].forEach((v) {
            message.add(new Message.fromJson(v));
          });
    
        }
      }
    
      Map<String, dynamic> toJson() {
        final Map<String, dynamic> data = new Map<String, dynamic>();
        data['status'] = this.status;
        if (this.message != null) {
    
          if(this.message is String) {
            data['message'] = this.message;
          }else {
            data['message'] = this.message.map((v) => v.toJson()).toList();
          }
        }
        return data;
      }
    }
    
    class Message {
      String updatedOn;
    
      Message({this.updatedOn});
    
      Message.fromJson(Map<String, dynamic> json) {
        updatedOn = json['updated_on'];
      }
    
      Map<String, dynamic> toJson() {
        final Map<String, dynamic> data = new Map<String, dynamic>();
        data['updated_on'] = this.updatedOn;
        return data;
      }
    }
    

    And call this way :

    MainResponse mainResponse = MainResponse.fromJson(response);
    

    and this :

     if(mainResponse.message is String) {
          var data = mainResponse.message as String;
          print('parsed String $data');
        }else {
    
          var list = mainResponse.message as List<Message>;
          print('parsed ${list[0].updatedOn }');
        }