I'm following this tutorial from devs docs to fetch data from internet but I can't decode response to User object.
Since I'm using Postman to check API I can tell you that my request is successfully received and server responses me with 200 and a body full of data (name, id and token) but when I try to call fromJson method inside try-catch block to create User object it fails and this error is printed :
flutter: Response 200
flutter: type '_InternalLinkedHashMap<String, dynamic>' is not a subtype of type 'String'
This is my User class :
class User extends Equatable {
final String token;
final String name;
final String id;
const User({
required this.token,
required this.name,
required this.id,
});
static const User empty = User(token: "token", name: "name", id: "id");
Map<String, dynamic> toMap() {
return <String, String>{
'token': token,
'name': name,
'id': id,
};
}
factory User.fromMap(Map<String, dynamic> json) {
return User(
token: json['token'] ?? "token",
name: json['name'] ?? "name",
id: json['id'] ?? "id",
);
}
String toJson() => json.encode(toMap());
factory User.fromJson(Map<String, dynamic> jsonMap) => User.fromMap(jsonMap);
#override
String toString() {
return token + ' ' + name + ' ' + id + '\n';
}
#override
List<Object?> get props => [token, name, id];
}
UserCredentials that's just a wrapper for username and password :
class UserCredentials {
final String name;
final String email;
final String password;
UserCredentials(
{required this.name, required this.email, required this.password});
factory UserCredentials.fromJson(Map<String, dynamic> json) {
return UserCredentials(
name: json['name'] as String,
email: json['email'] as String,
password: json['password'] as String,
);
}
Map<String, String> toJsonRegistration() {
return {
"name": name,
"email": email,
"password": password,
};
}
Map<String, String> toJsonLogin() {
return {
"email": email,
"password": password,
};
}
#override
String toString() {
return name + " " + email + " " + password + '\n';
}
}
And this is my user_repository function used to get data from server :
Future<User> _login(UserCredentials user) async {
User? userFetched;
//print(user);
http.Response response = await http.post(
Uri.parse("http://link_to_repo"),
body: user.toJsonLogin(),
);
print("Inizio");
print((response.body));
print(jsonDecode(response.body));
print(User.fromJson(jsonDecode(response.body)));
print("fine");
if (response.statusCode == 200) {
print(response.statusCode);
try {
userFetched = User.fromJson(jsonDecode(response.body));
print(userFetched);
} catch (e) {
print(e);
}
print("End");
return userFetched!;
} else {
print("Login Failure from repository");
throw Exception("Email already taken");
}
}
As you seen above it's able to print "Response 200" but not to complete successfully the operation inside try-catch block.
Edit :
This is the response.body :
{"data" : {"token":"50|IUMNqKgc7Vffmz8elRd0MIZeSyuEgHL418KwQ0Jz","name":"test","id":1}}
And jsonDecode(response.body)) :
{data: {token: 50|IUMNqKgc7Vffmz8elRd0MIZeSyuEgHL418KwQ0Jz, name: test, id: 1}}
Solution :
This post is a little confusing but to be clear :
Clucera gave me the exact solution to fetch the data and create User object so I accepted its answers.
Josip Domazet's answer helped me fixing the error but he didn't solve the problem because I still couldn't create User data.
Looking at your code it looks like you are doing the following
User.fromJson(jsonDecode(response.body));
and from there you are creating the User instance with the following factory
factory User.fromMap(Map<String, dynamic> json) {
return User(
token: json['token'] ?? "token",
name: json['name'] ?? "name",
id: json['id'] ?? "id",
);
The issue is that your Json is contained in another json called "data" as your print response show
{data: {token: 50|IUMNqKgc7Vffmz8elRd0MIZeSyuEgHL418KwQ0Jz, name: test, id: 1}}
in this case you have two solution, either you nest the keys inside your constructor (don't suggest you this approach)
factory User.fromMap(Map<String, dynamic> json) {
return User(
token: json['data']['token'] ?? "token",
name: json['data']['name'] ?? "name",
id: json['data']['id'] ?? "id",
);
Or where you parse the json you can extract directly the data json (I suggest you this option)
User.fromJson(jsonDecode(response.body)['data']);
jsonDecode(response.body) will return a Map. However your constructor User.fromJson takes a String as argument. Decoding it once is enough and from then on your can work with the Map you already have. So no need to call jsonDecode again in fromJson, this should work fine:
factory User.fromJson(Map<String, dynamic> jsonMap) =>
User.fromMap(jsonMap);
If we look at your exception it tell us just that:
flutter: type '_InternalLinkedHashMap<String, dynamic>' is not a subtype of type 'String'
Your constructor expects a String but received a Map (_InternalLinkedHashMap).
Related
I'm trying to store my whole JSON response in the dart model class. but the response is not stored in the model.
I got this error in my debug console
this is my JSON response
{
"status": "success",
"message": "retrived successfully",
"data": [
{
"id": 1,
"name": "NEET RAJKOT",
"logo": "https://via.placeholder.com/150/92c952"
},
{
"id": 2,
"name": "MEDICAL",
"logo": "https://via.placeholder.com/150/771796"
},
{
"id": 3,
"name": "NEET",
"logo": "https://via.placeholder.com/150/24f355"
}
]
}
This code is how to I get response and store in model class
response = await http.get(
Uri.parse('api_url'),
headers: {""});
String data = json.decode(response.body);
CoursesModel coursesModel = CoursesModel.fromJson(data);
List<Data> coursesData = coursesModel.data;
This my model class
import 'dart:convert';
import 'package:flutter/foundation.dart';
class CoursesModel {
String status;
String message;
List<Data> data;
CoursesModel({
required this.status,
required this.message,
required this.data,
});
CoursesModel copyWith({
String? status,
String? message,
List<Data>? data,
}) {
return CoursesModel(
status: status ?? this.status,
message: message ?? this.message,
data: data ?? this.data,
);
}
Map<String, dynamic> toMap() {
return <String, dynamic>{
'status': status,
'message': message,
'data': data.map((x) => x.toMap()).toList(),
};
}
factory CoursesModel.fromMap(Map<String, dynamic> map) {
return CoursesModel(
status: map['status'] as String,
message: map['message'] as String,
data: List<Data>.from((map['data'] as List<int>).map<Data>((x) => Data.fromMap(x as Map<String,dynamic>),),),
);
}
String toJson() => json.encode(toMap());
factory CoursesModel.fromJson(String source) => CoursesModel.fromMap(json.decode(source) as Map<String, dynamic>);
#override
String toString() => 'CoursesModel(status: $status, message: $message, data: $data)';
#override
bool operator ==(Object other) {
if (identical(this, other)) return true;
return other is CoursesModel &&
other.status == status &&
other.message == message &&
listEquals(other.data, data);
}
#override
int get hashCode => status.hashCode ^ message.hashCode ^ data.hashCode;
}
class Data {
int id;
String name;
String logo;
Data({
required this.id,
required this.name,
required this.logo,
});
Data copyWith({
int? id,
String? name,
String? logo,
}) {
return Data(
id: id ?? this.id,
name: name ?? this.name,
logo: logo ?? this.logo,
);
}
Map<String, dynamic> toMap() {
return <String, dynamic>{
'id': id,
'name': name,
'logo': logo,
};
}
factory Data.fromMap(Map<String, dynamic> map) {
return Data(
id: map['id'] as int,
name: map['name'] as String,
logo: map['logo'] as String,
);
}
String toJson() => json.encode(toMap());
factory Data.fromJson(String source) => Data.fromMap(json.decode(source) as Map<String, dynamic>);
#override
String toString() => 'Data(id: $id, name: $name, logo: $logo)';
#override
bool operator ==(Object other) {
if (identical(this, other)) return true;
return other is Data &&
other.id == id &&
other.name == name &&
other.logo == logo;
}
#override
int get hashCode => id.hashCode ^ name.hashCode ^ logo.hashCode;
}
use json_serializable (https://pub.dev/packages/json_serializable) package.
annotate class with #JsonSerilzable (and field with #JsonKey if needed). see the documentation for details.
I'm giving a lot of effort then I got a solution
1st I change fromJson to fromMap and my response takes as a Map
data = json.decode(coursesResponse.body);
coursesModel = CoursesModel.fromMap(data as Map<String,dynamic>);
coursesData = coursesModel.data!;
2nd Change is in the Model class
old-line code in fromMap Method List has List int
factory CoursesModel.fromMap(Map<String, dynamic> map) {
return CoursesModel(
status: map['status'] != null ? map['status'] as String : null,
message: map['message'] != null ? map['message'] as String : null,
data: map['data'] != null ? List<Data>.from((map['data'] as List<int>).map<Data?>((x) => Data.fromMap(x as Map<String,dynamic>),),) : null,
);
}
to change List dynamic
factory CoursesModel.fromMap(Map<String, dynamic> map) {
return CoursesModel(
status: map['status'] != null ? map['status'] as String : null,
message: map['message'] != null ? map['message'] as String : null,
data: map['data'] != null ? List<Data>.from((map['data'] as List<dynamic>).map<Data?>((x) => Data.fromMap(x as Map<String,dynamic>),),) : null,
);
}
I have following model class.
#JsonSerializable()
class Login {
String? userName;
String? password;
Login({required this.userName, required this.password});
factory Login.fromJson(Map<String, dynamic> json) => _$LoginFromJson(json);
Map<String, dynamic> toJson() => _$LoginToJson(this);
}
Generated part file.
Login _$LoginFromJson(Map<String, dynamic> json) => Login(
userName: json['userName'] as String?,
password: json['password'] as String?,
);
Map<String, dynamic> _$LoginToJson(Login instance) => <String, dynamic>{
'userName': instance.userName,
'password': instance.password,
};
when i try to use it to post to api with follow code
Future<void> loginUser(Login login) async => {
print(login.toJson().toString());
}
Result from the print statement (cause convertion error)
{userName: test, password: password}
Expecting valid json to post is
{
"username": "string",
"password": "string"
}
Error Message
Error: DioError [DioErrorType.other]: Converting object to an encodable object failed: Instance of '_HashSet<String>'
Remove the => from
Future<void> loginUser(Login login) async => {
print(login.toJson().toString());
}
to
Future<void> loginUser(Login login) async {
print(login.toJson().toString());
}
In the first example, it is an arrow function where the brackets { } stand for a set literal since it's right after the arrow, much like in the generated _$LoginToJson function but without describing the types of the set.
In the second example, it is a normal function the brackets { } define the body of the function.
You might be looking to call jsonEncode (import dart:convert;) which takes a value, such as a Map<String, dynamic> and turns it into a String in json format.
Update the print statement as follows:
print(jsonEncode(login.toJson()));
While the generated functions parse the object to a format that is suitable for JSON, it doesn't turn it into an actual String. You'll need to encode it with jsonEncode. Similarly, when processing a JSON response, you'll need to jsonDecode the response body to get a Map<String, dynamic> structure that you can then pass into the generated fromJson functions.
First of all, I am extremely new to working with HTTP Requests and stuff.
I jave created an API in Java Spring, and I have a bunch of Fitness Programmes assigned to a User, but this error will occur when I try to get the Object from the JSON in Flutter. I use the http and convert dart packages.
List is not a subtype of type 'Map<String,dynamic>
The problem is this line:
var userModel = Users.fromJson(jsonMap);
This is the getUsers Method:
Future<Users> getUsers() async{
var client = http.Client();
var userModel = null;
var response = await client.get(Uri.parse(Strings.users_url));
try {
if (response.statusCode == 200) {
var jsonString = response.body;
var jsonMap = json.decode(jsonString);
print(jsonMap);
var userModel = Users.fromJson(jsonMap);
}
}
catch(Exception) {
print(Exception);
return userModel;
}
return userModel;
}
These are the Users and Fitness Programme classes:
// To parse this JSON data, do
//
// final users = usersFromJson(jsonString);
import 'dart:convert';
List<Users> usersFromJson(String str) => List<Users>.from(json.decode(str).map((x) => Users.fromJson(x)));
String usersToJson(List<Users> data) => json.encode(List<dynamic>.from(data.map((x) => x.toJson())));
class Users{
Users({
this.id,
this.email,
this.password,
this.firstName,
this.lastName,
this.birthday,
this.weight,
this.height,
this.fitnessProgrammes,
});
int id;
String email;
String password;
String firstName;
String lastName;
dynamic birthday;
int weight;
int height;
List<FitnessProgramme> fitnessProgrammes;
factory Users.fromJson(Map<String, dynamic> json) => Users(
id: json["id"],
email: json["email"],
password: json["password"],
firstName: json["firstName"],
lastName: json["lastName"],
birthday: json["birthday"],
weight: json["weight"],
height: json["height"],
fitnessProgrammes: List<FitnessProgramme>.from(json["fitnessProgrammes"].map((x) => FitnessProgramme.fromJson(x))),
);
Map<String, dynamic> toJson() => {
"id": id,
"email": email,
"password": password,
"firstName": firstName,
"lastName": lastName,
"birthday": birthday,
"weight": weight,
"height": height,
"fitnessProgrammes": List<dynamic>.from(fitnessProgrammes.map((x) => x.toJson())),
};
}
class FitnessProgramme {
FitnessProgramme({
this.id,
this.name,
this.trainingDifficulty,
});
int id;
String name;
String trainingDifficulty;
factory FitnessProgramme.fromJson(Map<String, dynamic> json) => FitnessProgramme(
id: json["id"],
name: json["name"],
trainingDifficulty: json["trainingDifficulty"],
);
Map<String, dynamic> toJson() => {
"id": id,
"name": name,
"trainingDifficulty": trainingDifficulty,
};
}
Your API is probably returning an array of users instead of a single user. So the decoded JSON type is List<Map<String, dynamic>>. To resolve this, either loop through each user and instantiate them or make your API return just one specific user.
This code below will probably work and instantiate the first user returned by the API:
var userModel = Users.fromJson(jsonMap[0]);
When the app launch it triggers an API call and in my case, I want to store the response data for later use, so I used shared preferences for that and stored response data as a string. now I want to properly decode the data to use, from the stored string from shared preferences.
here is how I covert the data to string,
SharedPreferences prefs = await SharedPreferences.getInstance();
Response response = await _dio.post(
_baseUrl,
data: {"index": indexNum, "password": password},
options: Options(contentType: Headers.formUrlEncodedContentType),
);
if (response.statusCode == 200) {
var result = response.data;
//convert result data to string
var resultData = Result.encode(result);
// store the resultData in shared_preferences
prefs.setString('results', resultData);
}
encode method,
class Result {
Result({
required this.table,
required this.data,
});
String table;
List<Data> data;
factory Result.fromJson(Map<String, dynamic> json) => Result(
table: json["table"],
data: List<Data>.from(json["data"].map((x) => Data.fromJson(x))),
);
//encode method
static String encode(List<dynamic> results) => json.encode(
results.map((result) => result.toString()).toList(),
);
}
here is my approach to decode data from string,
getData() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
String? resultData = prefs.getString('results');
List<dynamic> decodedJson = jsonDecode(resultData!);
print(decodedJson);
}
resultData string,
resultData string after decode,
I am new to flutter and what I want is the proper way to decode this data from models. Below are my model classes.
import 'dart:convert';
class Result {
Result({
required this.table,
required this.data,
});
String table;
List<Data> data;
factory Result.fromJson(Map<String, dynamic> json) => Result(
table: json["table"],
data: List<Data>.from(json["data"].map((x) => Data.fromJson(x))),
);
static String encode(List<dynamic> results) => json.encode(
results.map((result) => result.toString()).toList(),
);
}
class Data {
Data({
required this.subjectName,
required this.year,
required this.credits,
required this.sOrder,
required this.result,
required this.onlineAssignmentResult,
});
String subjectName;
String year;
String credits;
String sOrder;
String result;
String onlineAssignmentResult;
factory Data.fromJson(json) => Data(
subjectName: json["subject_name"],
year: json["year"],
credits: json["credits"],
sOrder: json["s_order"],
result: json["result"],
onlineAssignmentResult: json["online_assignment_result"],
);
}
Appreciate your time and help.
Your JSON is in the wrong syntax for Flutter to decode with jsonDecode()
All strings and variable names need to be enclosed in " or '
I.e. {subject_name: should be {"subject_name":
hello everyone I'm trying to fetch JSON from API, but I'm getting this error while mapping the JSON into the model I created it type '_InternalLinkedHashMap<String, dynamic>' is not a subtype of type 'String'
this is the JSON response
{"data":{"userinfo":[{"firstname":"MAEN","lastname":"NASSAN","email":"maen.alnassan#outlook.com","active_status":0,"dark_mode":0,"messenger_color":"#2180f3","avatar":"avatar.png","gender":"male","region":"Turkey","birthyear":"2021","birthday":"1","birthmonth":"January","phonenumber":53105311,"category":"0","profilestatus":"private","ban":"0","banReason":"0","banDurationByDays":"0","email_verified_at":null,"created_at":"2021-05-24T16:27:52.000000Z","updated_at":"2021-05-24T16:27:52.000000Z"}],"userfriendsPosts":[{"postid":1,"userid":3,"posttitle":"Post 1 mohamed","post":"noattachment","likesCounter":0,"commentsCounter":0,"category":"0","created_at":"2021-05-22T20:49:48.000000Z","updated_at":"2021-05-22T20:49:48.000000Z"},{"postid":3,"userid":3,"posttitle":"Post 2 mohamed","post":"noattachment","likesCounter":0,"commentsCounter":0,"category":"0","created_at":"2021-05-22T20:58:40.000000Z","updated_at":"2021-05-22T20:58:40.000000Z"},{"postid":4,"userid":3,"posttitle":"Post 3 mohamed","post":"noattachment","likesCounter":0,"commentsCounter":0,"category":"0","created_at":"2021-05-22T20:58:43.000000Z","updated_at":"2021-05-22T20:58:43.000000Z"}],"usernotifications":[],"userlikes":[]}}
and this is the Model Class
import 'dart:convert';
Data dataFromJson(String str) => Data.fromJson(json.decode(str));
String dataToJson(Data data) => json.encode(data.toJson());
class Data {
Data({
this.data,
});
DataClass data;
factory Data.fromJson(Map<String, dynamic> json) => Data(
data: DataClass.fromJson(json["data"]),
);
Map<String, dynamic> toJson() => {
"data": data.toJson(),
};
}
class DataClass {
DataClass({
this.userinfo,
this.userfriendsPosts,
this.usernotifications,
this.userlikes,
});
List<Userinfo> userinfo;
List<UserfriendsPost> userfriendsPosts;
List<dynamic> usernotifications;
List<dynamic> userlikes;
factory DataClass.fromJson(Map<String, dynamic> json) => DataClass(
userinfo: List<Userinfo>.from(json["userinfo"].map((x) => Userinfo.fromJson(x))),
userfriendsPosts: List<UserfriendsPost>.from(json["userfriendsPosts"].map((x) => UserfriendsPost.fromJson(x))),
usernotifications: List<dynamic>.from(json["usernotifications"].map((x) => x)),
userlikes: List<dynamic>.from(json["userlikes"].map((x) => x)),
);
Map<String, dynamic> toJson() => {
"userinfo": List<dynamic>.from(userinfo.map((x) => x.toJson())),
"userfriendsPosts": List<dynamic>.from(userfriendsPosts.map((x) => x.toJson())),
"usernotifications": List<dynamic>.from(usernotifications.map((x) => x)),
"userlikes": List<dynamic>.from(userlikes.map((x) => x)),
};
}
class UserfriendsPost {
UserfriendsPost({
this.postid,
this.userid,
this.posttitle,
this.post,
this.likesCounter,
this.commentsCounter,
this.category,
this.createdAt,
this.updatedAt,
});
int postid;
int userid;
String posttitle;
String post;
int likesCounter;
int commentsCounter;
String category;
DateTime createdAt;
DateTime updatedAt;
factory UserfriendsPost.fromJson(Map<String, dynamic> json) => UserfriendsPost(
postid: json["postid"],
userid: json["userid"],
posttitle: json["posttitle"],
post: json["post"],
likesCounter: json["likesCounter"],
commentsCounter: json["commentsCounter"],
category: json["category"],
createdAt: DateTime.parse(json["created_at"]),
updatedAt: DateTime.parse(json["updated_at"]),
);
Map<String, dynamic> toJson() => {
"postid": postid,
"userid": userid,
"posttitle": posttitle,
"post": post,
"likesCounter": likesCounter,
"commentsCounter": commentsCounter,
"category": category,
"created_at": createdAt.toIso8601String(),
"updated_at": updatedAt.toIso8601String(),
};
}
class Userinfo {
Userinfo({
this.firstname,
this.lastname,
this.email,
this.activeStatus,
this.darkMode,
this.messengerColor,
this.avatar,
this.gender,
this.region,
this.birthyear,
this.birthday,
this.birthmonth,
this.phonenumber,
this.category,
this.profilestatus,
this.ban,
this.banReason,
this.banDurationByDays,
this.emailVerifiedAt,
this.createdAt,
this.updatedAt,
});
String firstname;
String lastname;
String email;
int activeStatus;
int darkMode;
String messengerColor;
String avatar;
String gender;
String region;
String birthyear;
String birthday;
String birthmonth;
int phonenumber;
String category;
String profilestatus;
String ban;
String banReason;
String banDurationByDays;
dynamic emailVerifiedAt;
DateTime createdAt;
DateTime updatedAt;
factory Userinfo.fromJson(Map<String, dynamic> json) => Userinfo(
firstname: json["firstname"],
lastname: json["lastname"],
email: json["email"],
activeStatus: json["active_status"],
darkMode: json["dark_mode"],
messengerColor: json["messenger_color"],
avatar: json["avatar"],
gender: json["gender"],
region: json["region"],
birthyear: json["birthyear"],
birthday: json["birthday"],
birthmonth: json["birthmonth"],
phonenumber: json["phonenumber"],
category: json["category"],
profilestatus: json["profilestatus"],
ban: json["ban"],
banReason: json["banReason"],
banDurationByDays: json["banDurationByDays"],
emailVerifiedAt: json["email_verified_at"],
createdAt: DateTime.parse(json["created_at"]),
updatedAt: DateTime.parse(json["updated_at"]),
);
Map<String, dynamic> toJson() => {
"firstname": firstname,
"lastname": lastname,
"email": email,
"active_status": activeStatus,
"dark_mode": darkMode,
"messenger_color": messengerColor,
"avatar": avatar,
"gender": gender,
"region": region,
"birthyear": birthyear,
"birthday": birthday,
"birthmonth": birthmonth,
"phonenumber": phonenumber,
"category": category,
"profilestatus": profilestatus,
"ban": ban,
"banReason": banReason,
"banDurationByDays": banDurationByDays,
"email_verified_at": emailVerifiedAt,
"created_at": createdAt.toIso8601String(),
"updated_at": updatedAt.toIso8601String(),
};
}
and this is the API request function
static Future<List<Data>> getData(token) async{
Dio dio = new Dio();
dio.options.headers["Authorization"] = "bearer $token";
dio.options.headers["Content-Type"] = 'application/json';
dio.options.headers["Accept"] = 'application/json';
await dio.get(url,).then((response) {
final List<Data> _data = dataFromJson(response.data) as List<Data>;
return _data;
}
).catchError((error) => print(error));
}
I tried all the method to fetch the data and the complex JSON List but it's always ending withe errors looks like this errors
The error you're getting is telling you: "your code expects a String, but you gave it a _InternalLinkedHashMap<String, dynamic> (i.e. a Map<String, dynamic>)".
It's not clear where the error is happening, but my guess is that response.data is a Map<String, dynamic>, but dataFromJson expects a String, and this line is causing the error.
If you look at the docs for Dio.get() (https://pub.dev/documentation/dio/latest/dio/Dio/get.html),
you can see the signature is:
Future<Response<T>> get<T>(String path, { ... });
When you call dio.get(url) without passing a type parameter, it defaults to dynamic, which essentially turns off type-checking. If you expect your api to return a String, you can provide that to dio by using: dio.get<String>(url).
However, if you're immediately going to jsonDecode it, you could modify dataFromJson to accept a Map<String, dynamic> rather than a String, and skip the jsonDecode step.
Finally, I'd recommend you to check out a json library (json_serializable is very easy to get started with). Hand writing json serialization code is repetitive and error-prone. Instead of manually writing your toJson() and fromJson() functions, you simply define stubs, and you can generate serialization code at compile time.
https://pub.dev/packages/json_serializable