Related
I'm trying to deserialize a json file and create instances from it but whatever way I use, I end up stucked because of the dynamic type :
type '_Map<String, dynamic>' is not a subtype of type 'Map<String, int>'
Here's my model :
class Race {
final String name;
final Map<String, int> abilitiesUpdater;
const Race({
required this.name,
required this.abilitiesUpdater
});
static fromJson(json) => Race(name: json['name'], abilitiesUpdater: json['abilitiesUpdater']);
}
Here's how I'm trying to deserialize the json file :
class RacesApi {
static Future<List<Race>> getRacesLocally(BuildContext context) async {
final assetBundle = DefaultAssetBundle.of(context);
final String fileContent = await assetBundle.loadString('Assets/COC_Monstres/Races.json');
List<dynamic> parsedListJson = jsonDecode(fileContent);
List<Race> racesList = List<Race>.from(parsedListJson.map<Race>((dynamic i) => Race.fromJson(i)));
return racesList;
}
}
Here's my json file :
[
{
"name": "Vampire",
"abilitiesUpdater": {
"DEX": 2,
"CHA": 2
}
},
{
"name": "Créature du lagon",
"abilitiesUpdater": {
"FOR": 2,
"CON": 2
}
},
...
]
How can I properly cast this json object to fit into my class ?
This works:
class Race {
final String name;
// changed to dynamic
final Map<String, dynamic> abilitiesUpdater;
const Race({required this.name, required this.abilitiesUpdater});
static fromJson(json) =>
Race(name: json['name'], abilitiesUpdater: json['abilitiesUpdater']);
}
Maybe after get the object you can parse that dynamic into int if you need it.
Change your Model Class to this:
class Race {
Race({
required this.name,
required this.abilitiesUpdater,
});
late final String name;
late final AbilitiesUpdater abilitiesUpdater;
Race.fromJson(Map<String, dynamic> json){
name = json['name'];
abilitiesUpdater = AbilitiesUpdater.fromJson(json['abilitiesUpdater']);
}
Map<String, dynamic> toJson() {
final _data = <String, dynamic>{};
_data['name'] = name;
_data['abilitiesUpdater'] = abilitiesUpdater.toJson();
return _data;
}
}
class AbilitiesUpdater {
AbilitiesUpdater({
required this.FOR,
required this.CON,
});
late final int FOR;
late final int CON;
AbilitiesUpdater.fromJson(Map<String, dynamic> json){
FOR = json['FOR'];
CON = json['CON'];
}
Map<String, dynamic> toJson() {
final _data = <String, dynamic>{};
_data['FOR'] = FOR;
_data['CON'] = CON;
return _data;
}
}
you can cast the json['abilitiesUpdater'] as Map<String, int> because internally flutter will set it default as Map<String, dynamic>
Code
class Race {
final String name;
final Map<String, int> abilitiesUpdater;
const Race({
required this.name,
required this.abilitiesUpdater
});
static fromJson(json) => Race(name: json['name'], abilitiesUpdater: json['abilitiesUpdater']) as Map<String,int>;
}
it is working fine with me i tried it here is the link to the code https://dartpad.dev/?id=550918b56987552eb3d631ce8cb9e063.
If you still getting error you can try this
class Race {
final String name;
final Map<String, int> abilitiesUpdater;
Race({
required this.name,
required this.abilitiesUpdater
});
static fromJson(json) => Race(name: json['name'], abilitiesUpdater: (json['abilitiesUpdater']as Map<String,int>)) ;
}
or you can try this
class Race {
final String name;
final Map<String, int> abilitiesUpdater;
const Race({required this.name, required this.abilitiesUpdater});
static fromJson(json) => Race(
name: json['name'],
abilitiesUpdater: json['abilitiesUpdater']
.map((key, value) => MapEntry<String, int>(key, value as int)),
);
}
Edit : To have something a little bit more handy and scalable, I created an extension, and it works fine eventhough I have to cast twice the object...
My model :
// import my extension
class Race {
Race({
required this.name,
required this.abilitiesUpdater,
});
late final String name;
late final Map<String, int> abilitiesUpdater;
// late final AbilitiesUpdater abilitiesUpdater;
Race.fromJson(Map<String, dynamic> json){
name = json['name'];
abilitiesUpdater = (json['abilitiesUpdater'] as Map<String, dynamic>).parseToStringInt();
}
}
My extension :
extension Casting on Map<String, dynamic> {
Map<String, int> parseToStringInt() {
final Map<String, int> map = {};
forEach((key, value) {
int? testInt = int.tryParse(value.toString());
if (testInt != null) {
map[key] = testInt;
} else {
debugPrint("$value can't be parsed to int");
}
});
return map;
}
}
Once again, any help on cleaning this is appreciated !
Original answer :
Thanks to Sanket Patel's answer, I ended up with a few changes that made my code works. However I'm pretty clueless on why I can't directly cast a
Map<String, dynamic>
object into a
Map<String, int>
one.
Any info on this would be appreciated :)
Here's how I changed my model class in the end :
class Race {
Race({
required this.name,
required this.abilitiesUpdater,
});
late final String name;
late final AbilitiesUpdater abilitiesUpdater;
Race.fromJson(Map<String, dynamic> json){
name = json['name'];
abilitiesUpdater = AbilitiesUpdater.fromJson(json['abilitiesUpdater']);
}
}
class AbilitiesUpdater {
final Map<String, int> abilitiesUpdater = {};
AbilitiesUpdater.fromJson(Map<String, dynamic> json){
json.forEach((key, value) {
abilitiesUpdater[key] = int.parse(value.toString());
});
}
}
I want to deserialize the JSON file but I have no idea how to build a model from this file. I want to know if my code is right or wrong
{
"Thriller": [
{
"Death Clique": "https://www.youtube.com/watch?v=4Q9lTjqQeBU&ab_channel=VMovies"
},
{
"CID": "https://www.youtube.com/watch?v=4Q9lTjqQeBU&ab_channel=VMovies"
},
{
"Wrong Turn": "https://www.youtube.com/watch?v=9spc-dExLH0&ab_channel=SagarForever"
},
],
"Action Movie": [
{
"Nobody": "https://nobodymovie.com"
},
{
"Tenet": "https://tenetmovie.com"
},
],
"Romantic Movie": [
{
"Titanic": "https://titanicmovie.com"
},
{
"The Vow": "https://thevowmovie.com"
},
]
}
I have built this model to fetch keys like (thriller, action movie,) to display in the list and inside list to display the values (movie name) in grid view.
class MovieCategories {
final String? title;
final List<Movie>? subServices;
MovieCategories({ this.title, this.subServices});
factory MovieCategories.fromJson(Map<String, dynamic> json) {
var list = json.values as List;
List<Movie> imagesList = list.map((i) => Movie.fromJson(i)).toList();
return MovieCategories(
title: json.keys.toString(),
subServices: imagesList,
);
}
}
class Movie {
final String? title;
final String? url;
Movie({this.title, this.url});
factory Movie.fromJson(Map<String, dynamic> parsedJson){
return Movie(
title:parsedJson.keys.toString(),
url:parsedJson.values.toString()
);
}
}
I'd also recommend following the proper key-value-pair JSON convention, as in:
{
[
{
"genre":"thriller",
"movies": [
{
"title": "Nobody",
"url": "https://nobodymovie.com"
},
{
"title": "Tenet",
"url": "https://tenetmovie.com"
}
]
...
}
That way the mapping would be much more straight-forward:
class Movie {
final String? title;
final String? url;
Movie({this.title, this.url});
factory Movie.fromJson(Map<String, dynamic> parsedJson){
return Movie(
title: parsedJson['title'],
url: parsedJson['url']
);
}
}
And your MovieCategories*:
class MovieCategories {
final String? genre;
final List<Movie>? movies;
MovieCategories({this.genre, this.movies});
factory MovieCategories.fromJson(
String cat, <Map<String, String> genre) {
return MovieCategories(
genre: genre['genre'],
movies: genre['movies'].map((m) => Movie.fromJson(m)).toList(),
);
}
}
If your data are constant like that then you can try this code.
import 'dart:convert';
/// Map<String, List<Map<String, String>>>
class MovieCategories {
final String? title;
final List<Movie>? subServices;
MovieCategories({this.title, this.subServices});
factory MovieCategories.fromJson(
String cat, List<Map<String, String>>? movies) {
return MovieCategories(
title: cat,
subServices: movies?.map((m) => Movie.fromJson(m)).toList(),
);
}
static List<MovieCategories> fromJsonMaps(String jsonString) {
final _listOfMovies =
Map<String, List<Map<String, String>>>.from(jsonDecode(jsonString));
return _listOfMovies.keys
.map((cat) => MovieCategories.fromJson(cat, _listOfMovies[cat]))
.toList();
}
}
class Movie {
final String? title;
final String? url;
Movie({this.title, this.url});
factory Movie.fromJson(Map<String, String> parsedJson) {
return Movie(title: parsedJson.keys.first, url: parsedJson.values.first);
}
}
I have a JSON file with different and unique objects as Employee1, employee2 and so on. Now I want to read the JSON file using the unique objects and then map the object contents to a variable and use it in my project.
the JSON file is as follows:
[
{
"employee1": {
"firstName": "Lokesh",
"lastName": "Gupta",
"website": "howtodoinjava.com"
}
},
{
"employee2": {
"firstName": "Brian",
"lastName": "Schultz",
"website": "example.com"
}
}
]
Just check out this example that I have created:
Following is the json that you provided.
[
{
"employee1": {
"firstName": "Lokesh",
"lastName": "Gupta",
"website": "howtodoinjava.com"
}
},
{
"employee2": {
"firstName": "Brian",
"lastName": "Schultz",
"website": "example.com"
}
}
]
This is the solution :
import 'dart:convert';
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(home: HomePage());
}
}
class Employee {
String firstName;
String lastName;
String website;
Employee({this.firstName, this.lastName, this.website});
}
class HomePage extends StatefulWidget {
#override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
bool _isLoading = false;
List<Employee> employeeList = List();
#override
void initState() {
super.initState();
getData();
}
getData() async {
String data =
await DefaultAssetBundle.of(context).loadString("json/parse.json");
var jsonData = json.decode(data);
jsonData.forEach((item) {
item.forEach((key, value) {
employeeList.add(Employee(
firstName: value['firstName'],
lastName: value['lastName'],
website: value['website']));
});
});
print(employeeList.length);
employeeList.forEach((item) {
print(item.firstName);
print(item.lastName);
print(item.website);
});
}
#override
Widget build(BuildContext context) {
return Scaffold(body: Text(''));
}
}
Let me know if it works.
feed the above json list to json.decode()
var dJson = json.decode(list);
//which results in dJson being a List<dynamic>
//access the first result
var first = dJson[0];
//which gives you a Map because the json.decode maintains the structure of the json given
var employee1 = first["employee1"];
//will give you another Map
var firstName = employee1["firstName"];
//will give you the firstName
print(firstName);//Lokesh
First, try to create a model out of the JSON you are getting. For the sample provide the Model, would look like this.
class EmployeeModel {
Employee1 employee1;
EmployeeModel({this.employee1});
EmployeeModel.fromJson(Map<String, dynamic> json) {
employee1 = json['employee1'] != null
? new Employee1.fromJson(json['employee1'])
: null;
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
if (this.employee1 != null) {
data['employee1'] = this.employee1.toJson();
}
return data;
}
}
class Employee1 {
String firstName;
String lastName;
String website;
Employee1({this.firstName, this.lastName, this.website});
Employee1.fromJson(Map<String, dynamic> json) {
firstName = json['firstName'];
lastName = json['lastName'];
website = json['website'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['firstName'] = this.firstName;
data['lastName'] = this.lastName;
data['website'] = this.website;
return data;
}
}
Then use
EmployeeModel employee = EmployeeModel.fromJson(responseJSON);
to access the values within the model.
I'm not able to convert server response to model class. The following is my code.
void main() {
//JSON to parse
var strJson = """{
\"person\": [
{\"name\": \"Mahendra\", \"age\": 28},
{\"name\": \"Test\", \"age\": 25}
]
}""";
var data = json.decode(strJson);
print("json: $data");
var result = PersonResponse<Person>.fromJSON(data);
print("result: ${result.persons}");
}
Model Class
class Person {
String name;
int age;
Person.fromJSON(Map json) {
this.name = json["name"];
this.age = json["age"];
}
}
class PersonResponse<T> {
List<T> persons;
PersonResponse.fromJSON(Map json) {
this.persons = json["person"];
}
}
When I run this code I'm not able to convert server response to model class. Getting following error...
Unhandled Exception: type List<dynamic> is not a subtype of type List<Person>
Whats wrong with my code. Any suggestions?
try
// To parse this JSON data, do
//
// final person = personFromJson(jsonString);
import 'dart:convert';
Person personFromJson(String str) => Person.fromJson(json.decode(str));
String personToJson(Person data) => json.encode(data.toJson());
class Person {
List<PersonElement> person;
Person({
this.person,
});
factory Person.fromJson(Map<String, dynamic> json) => Person(
person: List<PersonElement>.from(json["person"].map((x) => PersonElement.fromJson(x))),
);
Map<String, dynamic> toJson() => {
"person": List<dynamic>.from(person.map((x) => x.toJson())),
};
}
class PersonElement {
String name;
int age;
PersonElement({
this.name,
this.age,
});
factory PersonElement.fromJson(Map<String, dynamic> json) => PersonElement(
name: json["name"],
age: json["age"],
);
Map<String, dynamic> toJson() => {
"name": name,
"age": age,
};
}
I found 3 options:
You can either abandon usage of generics and replace List<T> persons; with List<dynamic> persons;. Your code will actually work with only this change.
You can abandon usage of generics and replace List<T> persons; with List<Person> and map it in fromJson.
class PersonResponse {
List<Person> persons;
PersonResponse.fromJSON(Map json) {
List<dynamic> list = json["person"];
persons = list.map((element) => Person.fromJSON(element)).toList();
}
}
Keep generics but restrict in to something serializable, to something like this:
class PersonResponse<T extends JsonSerializable> {
List<T> persons;
PersonResponse.fromJSON(Map json) {
List<dynamic> list = json["person"];
persons = list.map((element) => T.fromJSON(element)).toList();
}
}
Don't convert JSON into dart object manually. Because some time JSON response are very complex So it may happen you can write incorrect data type. So always use Online JSON to Dart Convertor. It is free to use. It will reduce the chance of error.
I have string like this,
{id:1, name: lorem ipsum, address: dolor set amet}
And I need to convert that string to json, how I can do it in dart flutter? thank you so much for your help.
You have to use json.decode. It takes in a json object and let you handle the nested key value pairs. I'll write you an example
import 'dart:convert';
// actual data sent is {success: true, data:{token:'token'}}
final response = await client.post(url, body: reqBody);
// Notice how you have to call body from the response if you are using http to retrieve json
final body = json.decode(response.body);
// This is how you get success value out of the actual json
if (body['success']) {
//Token is nested inside data field so it goes one deeper.
final String token = body['data']['token'];
return {"success": true, "token": token};
}
Create a model class
class User {
int? id;
String? name;
String? address;
User({this.id, this.name, this.address});
User.fromJson(Map<String, dynamic> json) {
id = json['id'];
name = json['name'];
address = json['address'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['id'] = this.id;
data['name'] = this.name;
data['address'] = this.address;
return data;
}
}
In the logic section
String data ='{id:1, name: lorem ipsum, address: dolor set amet}';
var encodedString = jsonEncode(data);
Map<String, dynamic> valueMap = json.decode(encodedString);
User user = User.fromJson(valueMap);
Also need to import
import 'dart:convert';
You can also convert JSON array to list of Objects as following:
String jsonStr = yourMethodThatReturnsJsonText();
Map<String,dynamic> d = json.decode(jsonStr.trim());
List<MyModel> list = List<MyModel>.from(d['jsonArrayName'].map((x) => MyModel.fromJson(x)));
And MyModel is something like this:
class MyModel{
String name;
int age;
MyModel({this.name,this.age});
MyModel.fromJson(Map<String, dynamic> json) {
name= json['name'];
age= json['age'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['name'] = this.name;
data['age'] = this.age;
return data;
}
}
String name = "{click_action: FLUTTER_NOTIFICATION_CLICK, sendByImage: https://ujjwalchef.staging-server.in/uploads/users/1636620532.png, status: done, sendByName: mohittttt, id: HM11}";
List<String> str = name.replaceAll("{","").replaceAll("}","").split(",");
Map<String,dynamic> result = {};
for(int i=0;i<str.length;i++){
List<String> s = str[i].split(":");
result.putIfAbsent(s[0].trim(), () => s[1].trim());
}
print(result);
}
You must need to use this sometimes
Map<String, dynamic> toJson() {
return {
jsonEncode("phone"): jsonEncode(numberPhone),
jsonEncode("country"): jsonEncode(country),
};
}
This code give you a like string {"numberPhone":"+225657869", "country":"CI"}. So it's easy to decode it's after like that
json.decode({"numberPhone":"+22565786589", "country":"CI"})
You must import dart:encode libary. Then use the jsonDecode function, that will produce a dynamic similar to a Map
https://api.dartlang.org/stable/2.2.0/dart-convert/dart-convert-library.html
For converting string to JSON we have to modify it with custom logic, in here first we remove all symbols of array and object and then we split text with special characters and append with key and value(for map).
Please try this code snippet in dartpad.dev
import 'dart:developer';
void main() {
String stringJson = '[{product_id: 1, quantity: 1, price: 16.5}]';
stringJson = removeJsonAndArray(stringJson);
var dataSp = stringJson.split(',');
Map<String, String> mapData = {};
for (var element in dataSp) {
mapData[element.split(':')[0].trim()] = element.split(':')[1].trim();
}
print("jsonInModel: ${DemoModel.fromJson(mapData).toJson()}");
}
String removeJsonAndArray(String text) {
if (text.startsWith('[') || text.startsWith('{')) {
text = text.substring(1, text.length - 1);
if (text.startsWith('[') || text.startsWith('{')) {
text = removeJsonAndArray(text);
}
}
return text;
}
class DemoModel {
String? productId;
String? quantity;
String? price;
DemoModel({this.productId, this.quantity, this.price});
DemoModel.fromJson(Map<String, dynamic> json) {
log('json: ${json['product_id']}');
productId = json['product_id'];
quantity = json['quantity'];
price = json['price'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = <String, dynamic>{};
data['product_id'] = productId;
data['quantity'] = quantity;
data['price'] = price;
return data;
}
}