Reputation: 409
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"
}
],
}
Upvotes: 2
Views: 2490
Reputation: 409
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());
Upvotes: 0
Reputation: 3584
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(),
);
Upvotes: 2