Parsing a List of Objects in Flutter error - json

I am using code generation to help with working with JSON files. I am wanting to parse a list of objects, but I keep getting the error:
[ERROR:flutter/lib/ui/ui_dart_state.cc(157)] Unhandled Exception: type 'List<dynamic>' is not a subtype of type 'Map<String, dynamic>'
The Json file I want to deserialize (I've already created models files for the json):
[
{
"id": "Nebula",
"setCount": "5 - 3",
"characters": {
"char1": "mario",
"char2": "peach"
},
"notes": "this player has really cool combos and float cancel nairs are cool"
},
{
"id": "Kambo",
"setCount": "2 - 8",
"characters": {
"char1": "fox",
"char2": ""
},
"notes": "I play fox for fun lmao"
},
{
"id": "Quiet",
"setCount": "8 - 3",
"characters": {
"char1": "zero_suit_samus",
"char2": "wolf"
},
"notes": "this player has really cool combos and float cancel nairs are cool"
}
]
I am not sure what I am doing wrong. Here is the where I call to receive a Playlist from the Json:
void setupHomePage() async {
PlayerList playerList = await getPlayerList();
Navigator.pushReplacement(
context,
MaterialPageRoute(
builder: (context) => Home(),
settings: RouteSettings(
arguments: playerList,
),
),
);
}
Decoding the JSON:
PlayerList playerList;
Future<String> _loadPlayerListAsset () async {
return await rootBundle.loadString('assets/nebula.json');
}
Future loadPlayerList () async {
String jsonString = await _loadPlayerListAsset();
final jsonResponse = jsonDecode(jsonString);
playerList = PlayerList.fromJson(jsonResponse);
}
Future<PlayerList> getPlayerList() async {
await loadPlayerList();
print(playerList.toString());
return playerList;
}
Generated JSON code:
#JsonSerializable(explicitToJson: true)
class PlayerList {
PlayerList (
this.players
);
List<Player> players;
factory PlayerList.fromJson (Map<String, dynamic> json) => _$PlayerListFromJson(json);
Map<String, dynamic> toJson() => _$PlayerListToJson(this);
}
PlayerList _$PlayerListFromJson(Map<String, dynamic> json) {
return PlayerList(
(json['players'] as List)
?.map((e) =>
e == null ? null : Player.fromJson(e as Map<String, dynamic>))
?.toList(),
);
}
Map<String, dynamic> _$PlayerListToJson(PlayerList instance) =>
<String, dynamic>{
'players': instance.players?.map((e) => e?.toJson())?.toList(),
};

The type of jsonResponse here:
final jsonResponse = jsonDecode(jsonString);
Is List since your JSON defines a list of maps.
But then you are doing:
playerList = PlayerList.fromJson(jsonResponse);
...
factory PlayerList.fromJson (Map<String, dynamic> json) => _$PlayerListFromJson(json);
The signature of your fromJson are saying it takes a Map<String, dynamic> which are not compatible with List.
The next problem will be json['players'] which are not going to work since your JSON does not contain any key with the name players. Also, your JSON are a List so it makes no sense.
Example on how to parse the provided JSON
I have make the following code which can parse the JSON you have provided as example:
import 'dart:convert';
class Player {
String id;
String setCount;
Map<String, String> characters;
String notes;
Player.fromJson(Map<String, dynamic> json)
: id = json['id'] as String,
setCount = json['setCount'] as String,
characters = (json['characters'] as Map).cast<String, String>(),
notes = json['notes'] as String;
static List<Player> getPlayersFromJson(List<dynamic> json) =>
json.cast<Map<String, dynamic>>().map((j) => Player.fromJson(j)).toList();
Map<String, dynamic> toJson() => <String, dynamic>{
'id': id,
'setCount': setCount,
'characters': characters,
'notes': notes
};
}
void main() {
const jsonString = '''
[
{
"id": "Nebula",
"setCount": "5 - 3",
"characters": {
"char1": "mario",
"char2": "peach"
},
"notes": "this player has really cool combos and float cancel nairs are cool"
},
{
"id": "Kambo",
"setCount": "2 - 8",
"characters": {
"char1": "fox",
"char2": ""
},
"notes": "I play fox for fun lmao"
},
{
"id": "Quiet",
"setCount": "8 - 3",
"characters": {
"char1": "zero_suit_samus",
"char2": "wolf"
},
"notes": "this player has really cool combos and float cancel nairs are cool"
}
]
''';
final playerList = Player.getPlayersFromJson(json.decode(jsonString) as List);
playerList.forEach((player) => print('${player.id} | ${player.notes}'));
// Nebula | this player has really cool combos and float cancel nairs are cool
// Kambo | I play fox for fun lmao
// Quiet | this player has really cool combos and float cancel nairs are cool
print(json.encode(playerList));
}

Related

Try to access in flutter a map inside a map from json but won´t work

i call an api with
Future<ApiData> fetchData() async {
final response = await http
.get(Uri.parse(
'https://apilink.com'));
if (response.statusCode == 200) {
Map<String, dynamic> jsonResponse = json.decode(response.body);
return ApiData.fromJson(jsonDecode(response.body));
}
and have a json file like this:
{
"data": {
"base_data": {
"US": {
"org_data": {
"country_full_name": "UNITED STATES",
"size": "large"
},
"user_data": {
"amount": 1.0,
"owned_in_total_euro": 100
}
},
"DE": {
"org_data": {
"country_full_name": "Germany",
"size": "medium"
},
"user_data": {
"amount": 5.0,
"owned_in_total_euro": 400
}
}
},
"calc_data": {
"country": {
"United States": 1.0
"Germany": 5.0
"Poland": 4.5
},
"currency": {
"USD": 1.0
"EUR": 2.0
}
}
}
}
In jsonReponse is a map with 1 item => 0: "data" -> Map (2 items)
This is a map with maps inside with other maps or lists inside and they are dynamice like this:
Map
Map1
MapABC
MapXYZ
List
Map2
List
MapX
I tried a lot of things like this but nothing worked:
//data map
Map<String, dynamic> jsonResponse = json.decode(response.body);
//base_data & calc_data inside data
Map<dynamic, dynamic> jsonResponse2 = Map.from(jsonResponse);
Question:
How can I access this in flutter?
Do you have any examples of info on this for me?
I tryed a lot with stuff I found in google and the decode-help but can´t find out how to process a (dynamic)-map in a map.
Thanks in advance
Patrick
Update:
I added this to my model:
#JsonSerializable(explicitToJson: true)
class BackendCalcData {
List<BackendCalcDataCountry>? backendCountry;
List<BackendCalcDataCurrency>? backendCurrency;
BackendCalcData({
List<BackendCalcDataCountry>? backendCountry,
List<BackendCalcDataCurrency>? backendCurrency,
});
factory BackendCalcData.empty() {
return BackendCalcData();
}
factory BackendCalcData.fromJson(Map<String, dynamic> json) =>
_$BackendCalcDataFromJson(json);
Map<String, dynamic> toJson() => _$BackendCalcDataToJson(this);
}
#JsonSerializable()
class BackendCalcDataCountry {
String countryName;
double countryRatio;
BackendCalcDataCountry({
this.countryName = "",
this.countryRatio = 0,
});
factory BackendCalcDataCountry.fromJson(Map<String, dynamic> json) =>
_$BackendCalcDataCountryFromJson(json);
Map<String, dynamic> toJson() => _$BackendCalcDataCountryToJson(this);
}
#JsonSerializable()
class BackendCalcDataCurrency {
String currencyCode;
double currencyRatetoEuro;
BackendCalcDataCurrency({
this.currencyCode = "",
this.currencyRatetoEuro = 0,
});
factory BackendCalcDataCurrency.fromJson(Map<String, dynamic> json) =>
_$BackendCalcDataCurrencyFromJson(json);
Map<String, dynamic> toJson() => _$BackendCalcDataCurrencyToJson(this);
}
And got the generated code.

Flutter json Unhandled Exception type

i have a problem with a json response
Json it's:
{
"SData": {
"total": "1",
"STable": {
"year": "2020",
"S1Lists": [
{
"year": "2020",
"turn": "6",
"Ranking": [
{
"position": "1",
"Person": {
"personId": "paul",
"nationality": "none"
},
},
]
}
]
}
}
Dart code
if(response.statusCode == 200){
final result = response.data;
Iterable list = result['SData'];
print(list);
}else{
throw Exception("Fail!");
}
and i receive this error
Unhandled Exception: type '_InternalLinkedHashMap<String, dynamic>' is not a subtype of type 'Iterable<dynamic>'
how can i solve if, for example, i want to access the position field or the personId field
This happens because your json output is a Map<String, dynamic> and not a Iterable.
if (response.statusCode == 200) {
final result = response.data;
final Map<String, dynamic> map = result['SData'];
print(map );
} else {
throw Exception("Fail!");
}
Which will print this output
{
"total":1,
"STable":{
"year":2020,
"S1Lists":[
{
"year":2020,
"turn":6,
"Ranking":[
{
"position":1,
"Person":{
"personId":"paul",
"nationality":"none"
}
}
]
}
]
}
}
If you want to get the ranking.
final Map<String, dynamic> data = result ['SData'];
final Map<String, dynamic> table = data['STable']; // This is like result['SData']['STable']
final Map<String, dynamic> list = table['S1Lists']; // result['SData']['STable']['S1Lists']
final Map<String, dynamic> firstItem = list[0]; // result['SData']['STable']['S1Lists'][0]
final Map<String, dynamic> ranking = list['Ranking']; // result['SData']['STable']['S1Lists'][0]['Ranking']

Dart - type 'String' is not a subtype of type 'Iterable<dynamic>'

I am trying to parse a local JSON file in dart and to display its values.
user.json
[
{
"id": 1,
"name": "Greg",
"imageUrl": "assets/images/greg.jpg"
},
{
"id": 2,
"name": "James",
"imageUrl": "assets/images/james.jpg"
}
]
Here is my chats.json
[
{
"sender": "james",
"time": "5:30 PM",
"text": "Hey, how's it going? What did you do today?",
"isLiked": false,
"unread": true
},
{
"sender": "olivia",
"time": "4:30 PM",
"text": "Hey, how's it going? What did you do today?",
"isLiked": true,
"unread": false
}
]
Here is my Message class
class Message {
final User sender;
final String time;
final String text;
final bool isLiked;
final bool unread;
Message({
this.sender,
this.time,
this.text,
this.isLiked,
this.unread,
});
factory Message.fromJson(Map<String, dynamic> parsedJson){
return Message(
sender: User.fromJson(parsedJson['sender']),
time : parsedJson['time'],
text : parsedJson ['text'],
isLiked : parsedJson['isLiked'],
unread : parsedJson ['unread'],
);
}
static List<User> parseUsers(usersJson) {
List<User> usersList = new List<User>.from(usersJson);
return usersList;
}
}
Here is my user class
class User {
final int id;
final String name;
final String imageUrl;
User({
this.id,
this.name,
this.imageUrl,
});
factory User.fromJson(Map<String, dynamic> parsedJson){
return User(
id: parsedJson['id'],
name : parsedJson['name'],
imageUrl : parsedJson ['imageUrl']
);
}
}
Here is my latest_chat.dart (it's StatefulWidget)
List<Message> _messages = List<Message>();
Future<String> _loadChatsAsset() async {
return await rootBundle.loadString('assets/json/chats.json');
}
Future<List<Message>> loadChats() async {
String jsonString = await _loadChatsAsset();
var messages = List<Message>();
final messagesJson = json.decode(jsonString);
for (var messageJson in messagesJson){
messages.add(Message.fromJson(messageJson));
}
return messages;
}
#override
void initState() {
loadChats().then((value) {
setState(() {
_messages.addAll(value);
});
});
super.initState();
}
To print something from my JSON I usually do _messages[index].sender.name or _messages[index].text etc...
The above parsing method worked without nested objects (for User only).
With nested objects (User inside Message) I am not able to identify the source of the following error.
type 'String' is not a subtype of type 'Iterable'
It's happening on this line of code
List usersList = new List.from(usersJson);
I am not sure what I am missing. Can you please assist?
Thanks in advance
As can be see in your json you are getting just string as a sender but when you are creating Message object then User class requires three arguments.
Means that, as a sender in json there should a user object.
{
"id" : 1
"name" : "Viren"
"imageUrl" : 'someurl'
}
Your json and your desire output is not matching.
Moreover your following line is also wrong.
sender: parseUsers(parsedJson['sender']),
Change to this.
sender: User.fromJson(parsedJson['sender'])
Update:
Your json should be like below.
[
{
"sender": {
"id": 1,
"name": "Greg",
"imageUrl": "assets/images/greg.jpg"
},
"time": "5:30 PM",
"text": "Hey, how's it going? What did you do today?",
"isLiked": false,
"unread": true
},
{
"sender": {
"id": 2,
"name": "James",
"imageUrl": "assets/images/james.jpg"
},
"time": "4:30 PM",
"text": "Hey, how's it going? What did you do today?",
"isLiked": true,
"unread": false
}
]

How to get json array in Flutter/Dart

I would want to get the data for all "name" only from the array data.
I want to print(data['data']['name']);
But it returns this error:
Unhandled Exception: type 'String' is not a subtype of type 'int' of 'index'
But when I print(data['data']);, it will return all data from "data":
"data": [
{
"created_at": "2020-03-16 16:10:51",
"deleted_at": null,
"id": 2,
"is_active": 1,
"name": "Maybank",
"updated_at": "2020-03-16 16:18:06"
},
{
"created_at": "2020-03-16 16:27:37",
......
],
Call API Code
displayBanks(BuildContext context) async {
_callApi.refreshTokenApi(context);
var _addressUrl = '$_hostUrl/banks'; //API URL
final SharedPreferences prefs = await SharedPreferences.getInstance();
_accessToken = prefs.getString('access_token');
Response _response = await get(_addressUrl, headers: {
'Content-type': 'application/json',
'Accept': 'application/json',
'Authorization': 'Bearer $_accessToken'
});
var data;
data = jsonDecode(_response.body);
if (_response.statusCode == 200) {
print(data['data']['name']);
return data;
}
else {
print(_response.statusCode);
}
}
SAMPLE JSON DATA FROM API URL:
{
"data": [
{
"created_at": "2020-03-16 16:10:51",
"deleted_at": null,
"id": 2,
"is_active": 1,
"name": "Maybank",
"updated_at": "2020-03-16 16:18:06"
},
{
"created_at": "2020-03-16 16:27:37",
"deleted_at": null,
"id": 3,
"is_active": 1,
"name": "India International Bank (Malaysia) Berhad",
"updated_at": "2020-03-16 16:27:37"
},
{
"created_at": "2020-03-16 16:27:37",
"deleted_at": null,
"id": 4,
"is_active": 1,
"name": "National Bank of Abu Dhabi Malaysia Berhad",
"updated_at": "2020-03-16 16:27:37"
}
],
"links": {
"first": "https://demo.local/api/banks?page=1",
"last": "https://demo.local/api/banks?page=1",
"next": null,
"prev": null
},
"meta": {
"current_page": 1,
"from": 1,
"last_page": 1,
"path": "https://demo.local/api/banks",
"per_page": 5,
"to": 3,
"total": 3
}
}
Unhandled Exception: type 'String' is not a subtype of type 'int' of 'index'
The exception message explains the issue clearly.
The property 'name' is inside an object which itself placed in an array. So you first decode the array. Then access each object using the index (0..n), then from each object, you can read the 'name' property.
Here you go
class MyData {
final List<Data> data;
MyData({this.data});
factory MyData.fromJson(Map<String, dynamic> json) {
return MyData(
data: json['data'] != null ? (json['data'] as List).map((i) => Data.fromJson(i)).toList() : null,
);
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
if (this.data != null) {
data['data'] = this.data.map((v) => v.toJson()).toList();
}
return data;
}
}
class Data {
final String created_at;
final String deleted_at;
final int id;
final int is_active;
final String name;
final String updated_at;
Data({this.created_at, this.deleted_at, this.id, this.is_active, this.name, this.updated_at});
factory Data.fromJson(Map<String, dynamic> json) {
return Data(
created_at: json['created_at'],
deleted_at: json['deleted_at'],
id: json['id'],
is_active: json['is_active'],
name: json['name'],
updated_at: json['updated_at'],
);
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['created_at'] = this.created_at;
data['id'] = this.id;
data['is_active'] = this.is_active;
data['name'] = this.name;
data['updated_at'] = this.updated_at;
data['deleted_at'] = this.deleted_at;
return data;
}
}
The error makes sense. The 'data' attribute in your JSON is array. So, you'll have to pass index of the item to access 'name' attribute - something like - data['data'][0]['name'] to get 'Maybank'.
Ideally, you should have a class which creates the instance from the JSON. In this case, the code snippet will look like :
Banks banks = new Banks.fromJson(data)
Now, you can use sites like this to create a class definition (including.fromJson).

Flutter: How to map a Dictionary? Make dropdown options via dictionary

I need some help in flutter data mapping. I have a JSON object that is returning some fields. I have to create a form depending on those fields. The issue that I am facing right now is that I cannot map the JSON dictionary to my dropdown list.
So basically I want to create a dropdown options that are in form_field_options of my json.
Here is a code sample of what I am trying to active, JSON return by my server is:
{
"status": "200",
"request": "0",
"message": "Success",
"data": {
"assetID": "155",
"assetTitle": "TPO",
"formTitle": "Roof Asset",
"preFields": [
{
"unique_id": "uid_201955451258",
"form_name": "General Overview",
"form_field_type": "100",
"form_field_required": "0",
"form_field_private": "0",
"form_field_duplicate_times": "00",
"form_field_disabledrow": "0"
},
{
"unique_id": "uid_201939764918",
"form_name": "Asset ID",
"form_field_type": "5",
"form_field_required": "1",
"form_field_private": "0",
"form_field_duplicate_times": "00",
"form_field_disabledrow": "0"
},
{
"unique_id": "uid_201789014253",
"form_name": "Facility ID",
"form_field_type": "5",
"form_field_required": "0",
"form_field_private": "0",
"form_field_duplicate_times": "00",
"form_field_disabledrow": "0"
},
{
"unique_id": "uid_201996716360",
"form_name": "Location",
"form_field_type": "19",
"form_field_required": "0",
"form_field_private": "0",
"form_field_duplicate_times": "00",
"form_field_disabledrow": "0"
},
{
"unique_id": "uid_201941758250",
"form_name": "Developed Area Type",
"form_field_type": "1",
"form_field_required": "0",
"form_field_private": "0",
"form_field_duplicate_times": "00",
"form_field_options": {
"1": {
"opt_name": "Suburban",
"opt_weightage": ""
},
"2": {
"opt_name": "Urban",
"opt_weightage": ""
},
"3": {
"opt_name": "Rural",
"opt_weightage": ""
}
},
"form_field_disabledrow": "0"
}
]
}
}
And here is my form class (auto generate class form quicktype) is:
// To parse this JSON data, do
//
// final form = formFromJson(jsonString);
import 'dart:convert';
Form formFromJson(String str) => Form.fromJson(json.decode(str));
String formToJson(Form data) => json.encode(data.toJson());
class Form {
String status;
String request;
String message;
Data data;
Form({
this.status,
this.request,
this.message,
this.data,
});
factory Form.fromJson(Map<String, dynamic> json) => new Form(
status: json["status"],
request: json["request"],
message: json["message"],
data: Data.fromJson(json["data"]),
);
Map<String, dynamic> toJson() => {
"status": status,
"request": request,
"message": message,
"data": data.toJson(),
};
}
class Data {
String assetId;
String assetTitle;
String formTitle;
List<PreField> preFields;
Data({
this.assetId,
this.assetTitle,
this.formTitle,
this.preFields,
});
factory Data.fromJson(Map<String, dynamic> json) => new Data(
assetId: json["assetID"],
assetTitle: json["assetTitle"],
formTitle: json["formTitle"],
preFields: new List<PreField>.from(json["preFields"].map((x) => PreField.fromJson(x))),
);
Map<String, dynamic> toJson() => {
"assetID": assetId,
"assetTitle": assetTitle,
"formTitle": formTitle,
"preFields": new List<dynamic>.from(preFields.map((x) => x.toJson())),
};
}
class PreField {
String uniqueId;
String formName;
String formFieldType;
String formFieldRequired;
String formFieldPrivate;
String formFieldDuplicateTimes;
String formFieldDisabledrow;
Map<String, FormFieldOption> formFieldOptions;
PreField({
this.uniqueId,
this.formName,
this.formFieldType,
this.formFieldRequired,
this.formFieldPrivate,
this.formFieldDuplicateTimes,
this.formFieldDisabledrow,
this.formFieldOptions,
});
factory PreField.fromJson(Map<String, dynamic> json) => new PreField(
uniqueId: json["unique_id"],
formName: json["form_name"],
formFieldType: json["form_field_type"],
formFieldRequired: json["form_field_required"],
formFieldPrivate: json["form_field_private"],
formFieldDuplicateTimes: json["form_field_duplicate_times"],
formFieldDisabledrow: json["form_field_disabledrow"],
formFieldOptions: json["form_field_options"] == null ? null : new Map.from(json["form_field_options"]).map((k, v) => new MapEntry<String, FormFieldOption>(k, FormFieldOption.fromJson(v))),
);
Map<String, dynamic> toJson() => {
"unique_id": uniqueId,
"form_name": formName,
"form_field_type": formFieldType,
"form_field_required": formFieldRequired,
"form_field_private": formFieldPrivate,
"form_field_duplicate_times": formFieldDuplicateTimes,
"form_field_disabledrow": formFieldDisabledrow,
"form_field_options": formFieldOptions == null ? null : new Map.from(formFieldOptions).map((k, v) => new MapEntry<String, dynamic>(k, v.toJson())),
};
}
class FormFieldOption {
String optName;
String optWeightage;
FormFieldOption({
this.optName,
this.optWeightage,
});
factory FormFieldOption.fromJson(Map<String, dynamic> json) => new FormFieldOption(
optName: json["opt_name"],
optWeightage: json["opt_weightage"],
);
Map<String, dynamic> toJson() => {
"opt_name": optName,
"opt_weightage": optWeightage,
};
}
Now when I try to apply loop or mapping on my options list (Which runs perfectly if its list) like this, It will not map it:
Column(
children: <Widget>[
FormBuilderDropdown(
attribute: item.uniqueId,
decoration:
InputDecoration(labelText: item.formName),
// initialValue: 'Male',
hint: Text(item.formName),
// validators: [
// FormBuilderValidators.required()
// ],
items: item.formFieldOptions.map((option) => DropdownMenuItem(
value: option,
child: Text("$option.optName")))
.toList(),
),
],
),
It throws me error that:
> The argument type '(String) → MapEntry<?, ?>' can't be assigned to the
> parameter type '(String, FormFieldOption) → MapEntry<dynamic,
> dynamic>'.dart(argument_type_not_assignable)
Please help or let me know what am I doing wrong. How can I make those dropdown options.
Thanks in advance
You sould try to map with the correct lambda signature (k, v) =>
items: item.formFieldOptions.map((key, option) => DropdownMenuItem(
value: key,
child: Text("${option.optName}"),
)
).toList()