Parsing JSON that has a nested array of objects in Dart? - json

I am making a Flutter app and I am using The MovieDB api to get data. When I call the api and ask for a specific movie, this is the general format that I get back:
{
"adult": false,
"backdrop_path": "/wrqUiMXttHE4UBFMhLHlN601MZh.jpg",
"belongs_to_collection": null,
"budget": 120000000,
"genres": [
{
"id": 28,
"name": "Action"
},
{
"id": 12,
"name": "Adventure"
},
{
"id": 878,
"name": "Science Fiction"
}
],
"homepage": "http://www.rampagethemovie.com",
"id": 427641,
"imdb_id": "tt2231461",
"original_language": "en",
"original_title": "Rampage",
...
}
I have setup a model class for to parse this and the class is defined as such:
import 'dart:async';
class MovieDetail {
final String title;
final double rating;
final String posterArtUrl;
final backgroundArtUrl;
final List<Genre> genres;
final String overview;
final String tagline;
final int id;
const MovieDetail(
{this.title, this.rating, this.posterArtUrl, this.backgroundArtUrl, this.genres, this.overview, this.tagline, this.id});
MovieDetail.fromJson(Map jsonMap)
: title = jsonMap['title'],
rating = jsonMap['vote_average'].toDouble(),
posterArtUrl = "http://image.tmdb.org/t/p/w342" + jsonMap['backdrop_path'],
backgroundArtUrl = "http://image.tmdb.org/t/p/w500" + jsonMap['poster_path'],
genres = (jsonMap['genres']).map((i) => Genre.fromJson(i)).toList(),
overview = jsonMap['overview'],
tagline = jsonMap['tagline'],
id = jsonMap['id'];
}
class Genre {
final int id;
final String genre;
const Genre(this.id, this.genre);
Genre.fromJson(Map jsonMap)
: id = jsonMap['id'],
genre = jsonMap['name'];
}
My issue is that I can't get the genre to parse properly from the JSON. When I get the JSON and pass it through my model class, I get the following error:
I/flutter (10874): type 'List<dynamic>' is not a subtype of type 'List<Genre>' where
I/flutter (10874): List is from dart:core
I/flutter (10874): List is from dart:core
I/flutter (10874): Genre is from package:flutter_app_first/models/movieDetail.dart
I thought this would work because I have made a different class for the Genre object and passed in the JSON array as a list. I don't understand how List<dynamic> isn't a child of List<Genre> because doesn't the keyword dynamic imply any object? Does anyone know how to parse a nested JSON array into custom objects?

Try genres = (jsonMap['genres'] as List).map((i) => Genre.fromJson(i)).toList()
The issue: calling map without the cast makes it a dynamic call, which means the return type from Genre.fromJson is also dynamic (not Genre).
Take a look at https://flutter.io/json/ for some hints.
There are solutions, like https://pub.dartlang.org/packages/json_serializable, that makes this much easier

I think JSONtoDart Converter is very useful, Must try...

After receiving the response, first of all, you need to extract the arrays separately. Then you can easily map. This is the way I do it.
List<Attempts> attempts;
attempts=(jsonDecode(res.body)['message1'] as List).map((i) => Attempts.fromJson(i)).toList();
List<Posts> posts;
attempts=(jsonDecode(res.body)['message2'] as List).map((i) => Post.fromJson(i)).toList();
Refer below example.
Future<List<Attempts>> getStatisticData() async {
String uri = global.serverDNS + "PaperAttemptsManager.php";
var res = await http.post(
uri,
headers: <String, String>{
'Content-Type': 'application/json; charset=UTF-8',
},
body: jsonEncode(<String, String>{
'userName': widget.userId,
'subject': widget.subjectName,
'method': "GETPTEN",
}),
);
if (res.statusCode == 200) {
List<Attempts> attempts;
attempts=(jsonDecode(res.body)['message'] as List).map((i) => Attempts.fromJson(i)).toList();
return attempts;
} else {
throw "Can't get subjects.";
}
}
Model Class
class Attempts {
String message, userName, date, year, time;
int status, id, marks, correctAnswers, wrongAnswers, emptyAnswers;
Attempts({
this.status,
this.message,
this.id,
this.userName,
this.date,
this.year,
this.marks,
this.time,
this.correctAnswers,
this.wrongAnswers,
this.emptyAnswers,
});
factory Attempts.fromJson(Map<String, dynamic> json) {
return Attempts(
status: json['status'],
message: json['message'],
id: json['ID'],
userName: json['USERNAME'],
date: json['DATE'],
year: json['YEAR'],
marks: json['MARKS'],
time: json['TIME'],
correctAnswers: json['CORRECT_ANSWERS'],
wrongAnswers: json['WRONG_ANSWERS'],
emptyAnswers: json['EMPTY_ANSWERS'],
);
}
}

Related

from json to object dart

Good afternoon, I may have found a small error, but I can't figure it out until now..
I have the following class in dart, the purpose of this class is to receive json and transform each field to object
class Attributes {
final String lpn; //itemId
final String type; //itemType (REEL,FEEDER,ETC)
final String feederContainer; //containerId
final String feederSide; //locationInContainer(A o B)
final String locationInTool; //locationInTool
Attributes(
{required this.lpn,
required this.type,
required this.feederContainer,
required this.feederSide,
required this.locationInTool});
factory Attributes.fromJson(Map json) {
return Attributes(
lpn: json['itemId'],
type: json['itemType'],
feederContainer: json['containerId'],
feederSide: json['locationInContainer'],
locationInTool: json['locationInTool'],
);
}
}
class PartNumberAt {
final String partNumber; //partNumber
final String quantity; //quantity
final String initialQuantity; //initialQuantity
PartNumberAt(
{required this.partNumber,
required this.quantity,
required this.initialQuantity});
factory PartNumberAt.fromJson(Map json) {
return PartNumberAt(
partNumber: json['partNumber'],
quantity: json['quantity'],
initialQuantity: json['initialQuantity'],
);
}
}
//partnumber RawMaterial
class ReelPartNumber {
final PartNumberAt partNumberAt;
ReelPartNumber({required this.partNumberAt});
factory ReelPartNumber.fromJson(Map json) {
return ReelPartNumber(
partNumberAt: PartNumberAt.fromJson(json['attributes']),
);
}
}
class ReelLpn {
Attributes? attributes;
ReelPartNumber? reelPartNumber;
ReelLpn(
{required Attributes attributes, required ReelPartNumber reelPartNumber});
factory ReelLpn.fromJson(Map json) {
return ReelLpn(
attributes: Attributes.fromJson(json['attributes']),
reelPartNumber: ReelPartNumber.fromJson(json['RawMaterial']),
);
}
}
and I have a file where I make http requests, the request returns the following
{
"attributes": {
"itemId": "0605007783",
"itemKey": "14992663",
"itemType": "REEL",
"itemTypeClass": "Component Lot",
"containerId": "FA0210AEF424292",
"locationInContainer": "B",
"toolContainerId": "SMT6",
"locationInTool": "10004-B",
"quarantineLocked": "false",
"expired": "false",
"initTmst": "2022-01-20T09:40:30.969-03:00"
},
"RawMaterial": {
"attributes": {
"partNumber": "11201312001166",
"partNumberDesc": "",
"supplierId": "DEFAULT",
"quantity": "2497.0",
"initialQuantity": "5000.0",
"rejectedQuantity": "3.0",
"manualAdjustmentQuantity": "548.0"
},
}
and the request is made as follows
Future<ReelLpn?> getReelData(String lpn) async {
http.Response response = await http.get(Uri.parse(apiUrl+'/$lpn'));
if (response.statusCode == 200) {
Map data = (json.decode(response.body)); //conver to json
print(data);
ReelLpn reelLpn = ReelLpn.fromJson(data); //transform json to ReelLpn
return reelLpn;
}
return null;
}
and I call the service as follows
ReelLpn? data = await CogiscanService().getReelData('0605007783');
print(data?.attributes?.type);
my problem starts there, when I print
print(data?.attributes?.type);
it returns null, I put several prints, in the ReelL class, Attributes and PartNumber, to see if I was reading the Map correctly, and they definitely read correctly.
So why when I want to access any of the fields does it return null?
Change your ReelLpn Constructor. you are not referencing the class members... thats why its always null.
ReelLpn({required this.attributes, required this.reelPartNumber});

Expected a value of type 'Map<String, dynamic>', but got one of type 'List<dynamic>'

I am new to flutter and I am developing a web app using flutter Web and RESTful API for backend. Am trying to make a get Request, however I am getting an error "Expected a value of type 'Map<String, dynamic>', but got one of type List",which have tried to debug based on the suggestions found here on stackOverflow but I can't seem to solve this issue. Here is a screenshot of the error am getting:
My API Response looks like this
{
"page": 1,
"pages": 1,
"pageSize": 20,
"totalRows": 3,
"estates": [ {
"id": "ccd442ae-471b-44c6-9eb3-e3c1dccab9f1",
"name": "sarova group of hotels",
"description": "An excuisite provider of luxury hotels and state–of–the–art apartments and villas.",
"rating": 4,
"city": {
"id": "1a861a5a-e793-45cb-9a41-71c5b538da05", "name": "mogadishu", "country": "somalia", "countryISO": "so", "latitude": -1.286389, "longitude": 35.817223, "picture": "https://images.adsttc.com/media/images/5016/4cd3/28ba/0d14/1600/0340/slideshow/stringio.jpg"
}
,
"startingPricePerNight": 0,
"checkInTime": "00:00:00",
"checkOutTime": "00:00:00",
"isVerified": false,
"estateGroups": []
}
,
]
}
And my API Response looks like this
Future < List < EstateSearch >> search(searchText, destination, rating, checkinDate,
checkoutDate, adult, child, infant, rooms, travelPurpose, page) async {
var searchUrl =
"${ApiEndPoint.baseUrl}/estate?q=$searchText&dest_type=$destination&rating=$rating&checkin_date=$checkinDate&checkout_date=$checkoutDate&no_adults=$adult&no_children=$child&no_infant=$infant&no_rooms=$rooms&travel_purpose=$travelPurpose&page=$page";
http.Response response = await http.get(
Uri.parse(searchUrl),
headers: {
"Content-Type": "application/json"
},
);
// print('ResonseBody' + '${response.body}');
List < EstateSearch > estateList = [];
var estates =
(jsonDecode(response.body) as Map < String, dynamic > )["estates"];
estateList = estates.map((i) => EstateSearch.fromJson(i)).toList();
return estateList;
}
And the serialized model looks like this
class EstateSearch extends Paginator {
String id;
String name;
String description;
int rating;
City city;
double startingPricePerNight;
String checkInTime;
String checkOutTime;
bool isVerified;
EstateGroup estateGroups;
EstateSearch({
required this.id,
required this.name,
required this.description,
required this.rating,
required this.city,
required this.startingPricePerNight,
required this.checkInTime,
required this.checkOutTime,
this.isVerified = false,
required this.estateGroups,
});
factory EstateSearch.fromJson(Map < String, dynamic > json) =>
_$EstateSearchFromJson(json);
Map < String, dynamic > toJson() => _$EstateSearchToJson(this);
}
try this for the return value on service
final data = json.decode(response.body);
return List<EstateSearch>.from(data['estates'].map((item) => EstateSearch.fromJson(item)));
I identified the mistake was in my model class(EstateSearch), where I was passing estateGroups of type "EstateSearch",whereas the API response was returning a List of estateGroups. So I only had to change the Model to this:
#JsonSerializable(explicitToJson: true)
class EstateSearch extends Paginator {
String id;
String name;
String description;
int rating;
City city;
double startingPricePerNight;
String checkInTime;
String checkOutTime;
bool isVerified;
List estateGroups = []; // replaced "EstateGroup estateGroups"
EstateSearch({
required this.id,
required this.name,
required this.description,
required this.rating,
required this.city,
required this.startingPricePerNight,
required this.checkInTime,
required this.checkOutTime,
this.isVerified = false,
required this.estateGroups,
});
factory EstateSearch.fromJson(Map < String, dynamic > json) =>
_$EstateSearchFromJson(json);
Map < String, dynamic > toJson() => _$EstateSearchToJson(this);
}

Problem with fetch: '_InternalLinkedHashMap<String, dynamic>' is not a subtype of type 'List<dynamic>'

I'm trying to fetch data from an API, but I keep getting this error.
Problem with fetch: '_InternalLinkedHashMap<String, dynamic>' is not a subtype of type 'List'
Please tell me how to fix this code.
Model.dart
class ComprovanteModel {
ComprovantesInfoModel jsonResponse;
String error;
ComprovanteModel({this.jsonResponse, this.error});
ComprovanteModel.fromJson(Map<String, dynamic> json)
: jsonResponse = ComprovantesInfoModel.fromJson(json['json_response']),
error = '';
ComprovanteModel.withError(String errorValue)
: jsonResponse = null,
error = errorValue;
}
class ComprovanteInfoModel {
String clientFormalName;
int volumes;
int duration;
CheckpointsModel checkpoint;
ComprovanteInfoModel({
this.clientFormalName,
this.duration,
this.volumes,
this.checkpoint,
});
ComprovanteInfoModel.fromJson(Map<String, dynamic> json)
: clientFormalName = json['client_formal_name'],
checkpoint = CheckpointsModel.fromJson(json['checkpoint']),
volumes = json['volumes'],
duration = json['duration'];
}
class CheckpointModel {
int checkpointId;
String arrivalTime;
int status;
CheckpointModel({
this.checkpointId,
this.arrivalTime,
this.status,
});
CheckpointModel.fromJson(Map<String, dynamic> json)
: checkpointId = json['checkpoint_id'],
arrivalTime = json['arrival_time'],
status = json['status'];
}
class CheckpointsModel {
List<CheckpointModel> checkpoint;
CheckpointsModel({this.checkpoint});
CheckpointsModel.fromJson(List<dynamic> jsonList)
: checkpoint = jsonList.map((e) => CheckpointModel.fromJson(e)).toList();
}
The API response:
{
"json_response": [
{
"client_formal_name": "",
"deadline": null,
"volumes": 1,
"duration": 5,
"depot_id": 20,
"service_id": 109856,
"georef_provider": "ap_geocoder",
"checkpoint": {
"checkpoint_id":,
"arrival_time": "",
"duration":,
"status": 1,
"event_id": 5,
"resources": [
{
"content_type": "PHOTO",
"service_event_effect_id": 58,
"content": "em+ndG6XtE2unp",
"content_label": "",
"user_effect_unique_code": ""
},
{
"content_type": "RECEPTOR_INFO",
"service_event_effect_id": 61,
"content": "{\"user_relationship_unique_code\":\"\",\"is_expected_receiver\":\"true\",\"document\":\"65979973000240\",\"name\":\"",\"description\":\"",\"id\":\"1\"}",
"content_label": "",
"user_effect_unique_code": "2"
}
],
"event_description": "",
"operation_date": "",
"obs": "",
"is_assistant": false,
"image": "{\"description\": \"Documento\", \"photo\": \""}"
},
"final_attendance_window_b": null
}
]
}
I want to access the checkpoint item, then the resource item(which I think is the same process as the checkpoint). I am using the list but I don't think is right, I am suppose to use map but I don't know how. Please show me a way.
Change this:
ComprovanteModel.fromJson(Map<String, dynamic> json)
: jsonResponse = ComprovantesInfoModel.fromJson(json['json_response']),
error = '';
To this:
ComprovanteModel.fromJson(Map<String, dynamic> json)
: jsonResponse = ComprovantesInfoModel.fromJson(json['json_response'][0]), //added [0] here.
error = '';
If you look closely at your response, it does have the map that you need, but this map is actually inside a list, notice the square brackets [ ] around the {} in "json_response": [.
The map that you need to access, is at index[0] of this list, then everything will work fine.
Second thing, this:
CheckpointsModel.fromJson(List<dynamic> jsonList)
: checkpoint = jsonList.map((e) => CheckpointModel.fromJson(e)).toList();
}
You are telling Flutter that you will pass an object of type List<dynamic> , but in the json you post, "checkpoint": { is not a list, it's a map. But even so, this map has only one checkpoint.
To answer your last question
I wanna access the checkpoint item, then the resource item(wich i
think is the same process as the checkpoint).
"resources": [ is indeed a list of Maps. In your code you did not post your resources model, but I'm assuming you want a List<Resources> and not List<CheckPoint>, it'll look like this:
class SingleResourceModel {
String contentType;
int serviceId;
String content;
String contentLabel;
String uniqueCode;
SingleResourceModel({
this.contentType,
this.serviceId,
this.content,
this.contentLabel,
this.uniqueCode
});
SingleResourceModel.fromJson(Map<String, dynamic> json)
: contentType = json['content_type'],
serviceId = json['service_event_effect_id'],
content = json['content'];
contentLabel = json['content_label'],
uniqueCode = json['user_effect_unique_code'];
}
class ListResourceModel {
List<SingleResourceModel> resourcesList;
ListResourceModel({this.resourcesList});
ListResourceModel.fromJson(List<Map<String, dynamic>> jsonList)
: resourcesList = jsonList.map((e) => SingleResourceModel.fromJson(e)).toList();
}
Finally, you can modify your CheckPoint model, and add to it a ListResourceModel, to look like this in the end:
class CheckpointModel {
int checkpointId;
String arrivalTime;
int status;
ListResourceModel resourcesList;
CheckpointModel({
this.checkpointId,
this.arrivalTime,
this.status,
this.resourcesList
});
CheckpointModel.fromJson(Map<String, dynamic> json)
: checkpointId = json['checkpoint_id'],
arrivalTime = json['arrival_time'],
status = json['status'],
resourcesList= json['resources'];
}
Now, you should be all set.

Flutter converting Nested Object from Json returns null

I have a Nested Object like this just a bit bigger:
"name": "String",
"exercise": [
{
"index": 1,
}
],
"pause": [
{"index":2},
]
I convert the exercise and pause to a Json String and save them in a column in SQFLite.
The problem
When I read the Data everything works fine including the List (not nested) but both list's of nested Object are empty when I read a value of the nested object it gives an error.
item.exercise[0].index.toString()
Valid Value range is empty: 0
When I read only item.exercise.toString() it returns []. Without != null ? [...] : List<Exercise>() it also throws an error
Data I get from my Database (shortened)
List of:
[{name: number 1, id: 56, exercise: [{"index":1,"weightGoal":[15,16,17]}, {"index":3,"weightGoal":[15,16,17]}], pause: [{"index":2}]},{"index":4}]}]
What I do with it
Here I try to go through the list and convert it into a List of PlanModel:
List<PlanModel> list =
res.isNotEmpty ? res.map((c) => PlanModel.fromJson(c)).toList() : [];
return list;
Full model
PlanModel planModelFromJson(String str) => PlanModel.fromJson(json.decode(str));
String planModelToJson(PlanModel data) => json.encode(data.toJson());
class PlanModel {
PlanModel({
this.name,
this.id,
this.workoutDays,
this.pastId,
this.timesDone,
this.exercise,
this.pause,
});
String name;
int id;
List<String> workoutDays;
int pastId;
int timesDone;
List<Exercise> exercise;
List<Pause> pause;
factory PlanModel.fromJson(Map<String, dynamic> json) => PlanModel(
name: json["name"],
id: json["id"],
workoutDays: List<String>.from(jsonDecode(json["workoutDays"])),
pastId: json["pastId"],
timesDone: json["timesDone"],
exercise: json["Exercise"] != null ? new List<Exercise>.from(json["Exercise"].map((x) => Exercise.fromJson(x))): List<Exercise>(),
pause: json["Pause"] != null ? new List<Pause>.from(json["Pause"].map((x) => Pause.fromJson(x))): List<Pause>(),
);
Map<String, dynamic> toJson() => {
"name": name,
"id": id,
"workoutDays": List<dynamic>.from(workoutDays.map((x) => x)),
"pastId": pastId,
"timesDone": timesDone,
"Exercise": List<dynamic>.from(exercise.map((x) => x.toJson())),
"Pause": List<dynamic>.from(pause.map((x) => x.toJson())),
};
}
class Exercise {
Exercise({
this.index,
this.name,
this.goal,
this.repGoal,
this.weightGoal,
this.timeGoal,
this.setGoal,
});
int index;
String name;
int goal;
int repGoal;
List<int> weightGoal;
int timeGoal;
List<String> setGoal;
Exercise.fromJson(dynamic json) {
// anything that is wrapped around with this [] in json is converted as list
// anything that is wrapped around with this {} is map
index = json["index"];
name = json["name"];
goal = json["goal"];
repGoal = json["repGoal"];
weightGoal = json["weightGoal"] != null ? json["weightGoal"].cast<int>() : [];
timeGoal = json["timeGoal"];
setGoal = json["setGoal"] != null ? json["setGoal"].cast<String>() : [];
}
Map<String, dynamic> toJson() => {
"index": index,
"name": name,
"goal": goal,
"repGoal": repGoal,
"weightGoal": List<dynamic>.from(weightGoal.map((x) => x)),
"timeGoal": timeGoal,
"setGoal": List<dynamic>.from(setGoal.map((x) => x)),
};
}
class Pause {
Pause({
this.index,
this.timeInMilSec,
});
int index;
int timeInMilSec;
factory Pause.fromJson(Map<String, dynamic> json) => Pause(
index: json["index"],
timeInMilSec: json["timeInMilSec"],
);
Map<String, dynamic> toJson() => {
"index": index,
"timeInMilSec": timeInMilSec,
};
}
Read this first.
You need to tweek this code a little to work for you but the idea is that;
also read comment in the code.
if json string comes with [] those around, json.decode will decode it as List<Map>.
if it comes with {} this json.decode will decode it as Map.
note: be careful while using generics on json.decode I reccommend not to.
data inside the jsonString does not really corresponds with the values inside the fromJson function. json string which you have provided was not really good. so I think you will understand how to manipulate it for your needs.
also main constructor Exercise you can use for initial data.
import 'dart:convert';
class Exercise{
Exercise({this.index,
this.name,
this.repGoal,
this.weightGoal,
this.setGoal});
String index;
String name;
String repGoal;
String weightGoal;
String setGoal;
Exercise.fromJson(dynamic json) :
// anything that is wrapped around with this [] in json is converted as list
// anything that is wrapped around with this {} is map
index = json["exercise"][0]["index"].toString(),
name = json["name"].toString(),
repGoal = json["repGoal"].toString(),
weightGoal = json["weightGoal"].toString(),
setGoal = json["setGoal"].toString();
}
void main(){
String jsonString = '{name: number 1, id: 56, exercise: [{"index":1,"weightGoal":[15,16,17], pause: [{"index":2}]}';
Map json = json.decode(jsonString);
Exercise.fromJson(json);
}
I found it out :)
I have restructured my fromJson to this, especially the jsonDecode was important, because json["exercise "] was only a String.
PlanModel.fromJson(dynamic json) {
name = json["name"];
if (json["exercise"] != null) {
exercise = [];
jsonDecode(json["exercise"]).forEach((v) {
exercise.add(Exercise.fromJson(v));
});
}}
now I can access it with
PlanModel item = snapshot.data[index];
item.exercise[0].timeGoal.toString()

_CastError (type 'Client' is not a subtype of type 'List<dynamic>' in type cast)

My API returns the following response:
[{id: 1, nome: foo}, {id: 2, nome: bar}]
And I created a model Client to represent each one:
class Client {
final int id;
final String name;
Client({
this.id,
this.name,
});
factory Client.fromJson(Map<String, dynamic> json) {
return Client(
id: json['id'],
name: json['nome'],
);
}
Map<String, dynamic> toJson() => {
'id': id,
'nome': name,
};
}
Then, in my repository, the method fetching the data above is as follows:
Future<List<Client>> getClients() async {
try {
final _response = await _dio.get(
'/clientes',
options: Options(
headers: {'Authorization': 'Bearer $TOKEN'},
),
);
return Client.fromJson(_response.data[0]) as List; // Error pointed to this line
} on DioError catch (_e) {
throw _e;
}
}
Being stored here
#observable
List<Client> clients;
I am not sure what to do. What am I doing wrong?
dio will decode the response and you'll get a List<dynamic>. Use List.map to convert it to a list of clients, by passing a function that will turn a Map<String, dynamic> into a Client. (You already have one - the named constructor.)
For example:
var dioResponse = json.decode('[{"id": 1, "nome": "foo"}, {"id": 2, "nome": "bar"}]');
List<dynamic> decoded = dioResponse;
var clients = decoded.map<Client>((e) => Client.fromJson(e)).toList();
You're trying to cast a Client as a List<dynamic> which isn't a valid cast since Client doesn't implement List. If you want to return a List containing a single Client, you'll want to change the line with the error to:
return [Client.fromJson(_response.data[0])];