I am new to both Flutter and Dart and trying to convert some android studio application to use flutter if I can. I am trying to parse some simple json to learn how all of the dart/flutter features can help me.
The class structure that I want to write to is:
class Company extends Salinas {
final String name;
Company({this.name}) : super();
factory Company.fromJson(Map<String, dynamic> json) => _$CompanyFromJson(json);
Map<String, dynamic> toJson() => _$CompanyToJson(this);
}
class Salinas {
final int id;
Salinas({this.id});
factory Salinas.fromJson(Map<String, dynamic> json) => _$SalinasFromJson(json);
Map<String, dynamic> toJson() => _$SalinasToJson(this);
}
the json string is simple
{"id":1,"name":"Acme"}
and:
print(company.id)is null
print(company.name) is Acme;
when I look at the Company.g.dart file there is no reference to the extended class Salinas?
is there a way to do this?
I'm clearly missing something.
You need to define extended class properties in child constructor like this:
class Company extends Salinas {
final String name;
Company({id, this.name}) : super(id: id);
}
After you will see this result:
print(company.id) is 1
print(company.name) is Acme;
class Company {
int id;
String name;
Company({this.id, this.name});
Company.fromJson(Map<String, dynamic> json) {
id = json['id'];
name = json['name'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['id'] = this.id;
data['name'] = this.name;
return data;
}
}
On the basis of JSON you have provided, you should have made a Model as above.
After that, you can map the whole thing quite conveniently,
Company company = Company.fromJson(response);
and then you are free to print
print(company.id);
print(company.name);
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());
});
}
}
Im sure you know the problem above. Im wondering how I can solve it. I understand that my data is in form of a list but inside the data class I used map. I don't really understand how I should change it to work, basically I just followed the flutter.dev documentation
So if you are wondering what I did
I basically parsed my data with json_serializable. In testing with test data all worked fine.
My data:
My model contains a title, image & a nested class called modelData.
`import 'package:json_annotation/json_annotation.dart';
import 'modelData.dart';
part 'Modell.g.dart';
#JsonSerializable(explicitToJson: true)
class Modell {
final String title;
final String image;
final ModelData modelData;
Modell(this.title, this.image, this.modelData);
factory Modell.fromJson(Map<String, dynamic> json)
=> _$ModellFromJson(json);
Map<String, dynamic> toJson() => _$ModellToJson(this);}
`
import 'package:json_annotation/json_annotation.dart';
part 'modelData.g.dart';
#JsonSerializable()
class ModelData {
final String title;
ModelData(this.title);
factory ModelData.fromJson(Map<String, dynamic> json)
=> _$ModelDataFromJson(json);
Map<String, dynamic> toJson() => _$ModelDataToJson(this);
}
Im consuming the data with this code:
var modelle = const[];
Future loadDataList() async {
String content = await rootBundle.loadString("assets/ddddddd.json");
List collection = json.decode(content);
List<Modell> _modelle = collection.map((json) => Modell.fromJson(json)).toList();
setState(() {
modelle = _modelle;
});
}
void initState() {
loadDataList();
super.initState();
& if needed here a part of my Data:
[
{
"title":" Alfa Romeo ",
"image":" AlfaRomeo.png ",
"modelData":[
{
"title":" 4C ",
"variantenData":[
]
},
I hope I wrote clear & detailed enough. If Im missing something, sry
-----Update-----
I tested a bit & found out that with simply adding List I no longer get the error, will test further to see whether the solution really works as intended
import 'package:json_annotation/json_annotation.dart';
import 'modelData.dart';
part 'Modell.g.dart';
#JsonSerializable(explicitToJson: true)
class Modell {
final String title;
final String image;
List <ModelData> modelData; //adding List
Modell(this.title, this.image, this.modelData);
factory Modell.fromJson(Map<String, dynamic> json)
=> _$ModellFromJson(json);
Map<String, dynamic> toJson() => _$ModellToJson(this);
}
You have parsed the data and all but the class named Modell should contain a list of ModelData and not a single ModelData. Please change the type to a list of ModelData and it should work hopefully.
class Modell {
final String title;
final String image;
final List<ModelData> modelDataList;
Modell(this.title, this.image, this.modelDataList);
factory Modell.fromJson(Map<String, dynamic> json)
=> _$ModellFromJson(json);
Map<String, dynamic> toJson() => _$ModellToJson(this);}```
In this part:
var modelle = const[];
Future loadDataList() async {
String content = await rootBundle.loadString("assets/ddddddd.json");
List collection = json.decode(content);
List<Modell> _modelle = collection.map((json) => Modell.fromJson(json)).toList();
setState(() {
modelle = _modelle;
});
}
void initState() {
loadDataList();
super.initState();
More specific here:
List collection = json.decode(content);
If you go to Flutter Example will see that collection will return a Map<String, dynamic>.
Try change to:
Map<String, dynamic> collection = json.decode(content);
Kindly assist.
I am attempting to create a model using the JSON to Dart tool https://app.quicktype.io/
The aim is to retrieve a list of all companies (with their properties) from a JSON API.
I am getting the below errors:
the argument type 'dynamic' can't be assigned to the parameter type Map<String, dynamic>
the argument type 'dynamic' can't be assigned to the parameter type Iterable
the argument type 'dynamic' can't be assigned to the parameter type Int
Please refer to the image below.
the argument type 'dynamic' can't be assigned to the parameter type...
Thank you.
I have tested this class and it works without errors
import 'dart:convert';
CompanyModel companyModelFromJson(String str) => CompanyModel.fromJson(json.decode(str));
String companyModelToJson(CompanyModel data) => json.encode(data.toJson());
class CompanyModel {
CompanyModel({
this.companies,
});
List<Company> companies;
factory CompanyModel.fromJson(Map<String, dynamic> json) => CompanyModel(
companies: List<Company>.from(json["companies"].map((x) => Company.fromJson(x))),
);
Map<String, dynamic> toJson() => {
"companies": List<dynamic>.from(companies.map((x) => x.toJson())),
};
}
class Company {
Company({
this.id,
this.name,
});
int id;
String name;
factory Company.fromJson(Map<String, dynamic> json) => Company(
id: json["id"],
name: json["name"],
);
Map<String, dynamic> toJson() => {
"id": id,
"name": name,
};
}
As alternative you can use this code from JsonToDart
class CompanyModel {
List<Companies> companies;
CompanyModel({this.companies});
CompanyModel.fromJson(Map<String, dynamic> json) {
if (json['companies'] != null) {
companies = new List<Companies>();
json['companies'].forEach((v) {
companies.add(new Companies.fromJson(v));
});
}
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
if (this.companies != null) {
data['companies'] = this.companies.map((v) => v.toJson()).toList();
}
return data;
}
}
class Companies {
int id;
String name;
Companies({this.id, this.name});
Companies.fromJson(Map<String, dynamic> json) {
id = json['id'];
name = json['name'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['id'] = this.id;
data['name'] = this.name;
return data;
}
}
This is the parent which has an implemented method
abstract class CommonModel {
int id;
String name;
....
Map<String, dynamic> toJson(){
final Map<String, dynamic> data = new Map<String, dynamic>();
data['Id'] = this.id;
data['Name'] = this.name;
return data;
}
}
and this the the child class which i need to use the method [toJson] on it with its implementation and add more data.
import 'package:Elnota/models/governorate.dart';
import 'package:Elnota/models/abstract_common_model.dart';
class Centeral extends CommonModel{
Governorate gov;
Centeral({
int id,
String name,
this.gov}) : super(id:id, name:name);
Centeral.fromJson(Map<String, dynamic> json) :super.fromJson(json){
gov = json['Gov'] != null ? new Governorate.fromJson(json['Gov']) : null;
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['Gov'] = this.gov;
}
}
It's a little hard to understand what you need, but how about this:
#override
Map<String, dynamic> toJson() {
final data = super.toJson();
data['Gov'] = gov.toJson();
return data;
}
I have array of country json objects like this
[{"id":4,"name":"Afghanistan","alpha2":"af","alpha3":"afg"},
{"id":8,"name":"Albania","alpha2":"al","alpha3":"alb"},
{"id":12,"name":"Algeria","alpha2":"dz","alpha3":"dza"},..
I have got them to a map like this
Map<String, dynamic> jsonMap;
jsonMap = json.decode(jsonString);
After this I have the data like this
[{id: 4, name: Afghanistan, alpha2: af, alpha3: afg}, {id: 8, name: Albania, alpha2: al, alpha3: alb}, ...
What i want to do is create a List of county objects from this
Country
{
int id;
String name;
String alpha2;
String alpha3;
}
Can anyone explain how to do this conversion ?
The best way to do it is to make a PODO out of this,
class Country {
int id;
String name;
String alpha2;
String alpha3;
Country({this.id, this.name, this.alpha2, this.alpha3});
Country.fromJson(Map<String, dynamic> json) {
id = json['id'];
name = json['name'];
alpha2 = json['alpha2'];
alpha3 = json['alpha3'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['id'] = this.id;
data['name'] = this.name;
data['alpha2'] = this.alpha2;
data['alpha3'] = this.alpha3;
return data;
}
}
Now using this you can access the fromJson method and also use the toJson method.
I use this straight forward way:
List<TestModel> myTestModel;
var response = await http.get("myTestUrl");
myTestModel=(json.decode(response.body) as List).map((i) =>
TestModel.fromJson(i)).toList();
The link can be really helpful:
json_serializable