2

I have an API response payload with dynamic data in the body. The API returns a list of objects in the data tag. I am trying to map the response to the appropriate model at runtime, however, I get an error message when doing so. How do I map the dynamic response objects at runtime without explicitly creating an API response model for each? Ideally the solution should be able to determine the target model the response object should be mapped to at runtime.

I get the following error with my code: The argument type 'List<Map<String, dynamic>>' can't be assigned to the parameter type 'Map<String, dynamic>'.

Following my attempt:

return ApiResponse<Team>.fromJson(json.decode(response.body), (data) => Team.fromJson(data) as List<Map<String, dynamic>>);
// Maps the API response to object
class ApiResponse<T> {
  int status;
  String? message;
  T data;

  ApiResponse({
    required this.status,
    this.message,
    required this.data,
  });

  factory ApiResponse.fromJson(Map<String, dynamic> json, Function(List<Map<String, dynamic>>) create) {
    return ApiResponse<T>(
      status: json['status'],
      message: json['message'],
      data: create(json['data']),
    );
  }
}

My models


class User{
  int? id;
  String? name;
  String? description;
  DateTime? createdAt;
  DateTime? updatedAt;

  User({
    this.id,
    this.name,
    this.description,
    this.createdAt,
    this.updatedAt
  });

  factory User.fromJson(Map<String, dynamic> json){
    return User(
      id: json['id'],
      name: json['name'],
      description: json['description'],
      createdAt: DateTime.parse(json['created_at']),
      updatedAt: DateTime.parse(json['updated_at']),
    );
  }
}


class Team {
  int id;
  String? name;
  String? region;
  DateTime? createdAt;
  DateTime? updatedAt;

  Team({
    required this.id,
    this.name,
    this.region,
    this.createdAt,
    this.updatedAt,
  });

  factory Team.fromJson(Map<String, dynamic> json){
    return Team(
      id: json['id'],
      name: json['name'],
      region: json['region'],
      createdAt: DateTime.parse(json['created_at']),
      updatedAt: DateTime.parse(json['updated_at']),
    );
  }
}

API response


{
  "status": 200,
  "message": "Returned",
  "data": [
    {
      "id": 1,
      "name": "Trevo Mgino",
      "description": "A Description",
      "created_at": "2021-09-29T06:47:03.000000Z",
      "updated_at": "2021-09-29T06:47:03.000000Z"
    }
  ],
}
{
  "status": 200,
  "message": "Activated",
  "data": [
    {
      "id": 1,
      "name": "Team A",
      "region": "Region 1",
      "created_at": "2021-09-29T06:47:03.000000Z",
      "updated_at": "2021-09-29T06:47:03.000000Z"
    },
    {
      "id": 2,
      "name": "Team B",
      "region": "Region 1",
      "created_at": "2021-09-29T06:47:03.000000Z",
      "updated_at": "2021-09-29T06:47:03.000000Z"
    }
  ],
}
1
  • just decode your json and pass this json team.formjson(json) Commented Oct 4, 2021 at 11:47

2 Answers 2

2

Team.fromJson takes a Map<String, dynamic> as a parameters but you give it a List<Map<String, dynamic>>.

I think what you want is to change the first expression you gave to:

ApiResponse<Team>.fromJson(
  json.decode(response.body),
  (data) => data.map((teamJson) => Team.fromJson(teamJson)),
)

Also you could type safe your factory be using:

class ApiResponse<T> {
  int status;
  String? message;
  T data;

  ApiResponse({
    required this.status,
    this.message,
    required this.data,
  });

  factory ApiResponse.fromJson(
    Map<String, dynamic> json,
    T Function(List<Map<String, dynamic>>) create,
  ) {
    return ApiResponse<T>(
      status: json['status'],
      message: json['message'],
      data: create(json['data']),
    );
  }
}

If you do so you will also have to change your first expression again to write the right type (I think you made a mistake but maybe I'm wrong):

ApiResponse<List<Team>>.fromJson(
  json.decode(response.body),
  (data) => data.map((teamJson) => Team.fromJson(teamJson)).toList(),
);
Sign up to request clarification or add additional context in comments.

Comments

0

So I managed to resolve the issues with the following code:

// Maps the API response to object
class ApiResponse<T> {
  int status;
  String? message;
  T data;

  ApiResponse({
    required this.status,
    this.message,
    required this.data,
  });

  factory ApiResponse.fromJson(Map<String, dynamic> json, List<T> Function(List<dynamic>) create) {
    return ApiResponse<T>(
      status: json['status'],
      message: json['message'],
      data: create(json['data']),
    );
  }
}

Calling the mapper

return ApiResponse<Team>.fromJson(json.decode(response.body),
      (data) => data.map((tData) => Team.fromJson(tData)).toList());

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.