I want to improve the functioning of my application a bit using json_serializable. I use the https://www.thecocktaildb.com/api.php Here is the code for the model that is currently in the app.
`
class CocktailModel {
final String name;
final String category;
final String alcoholic;
final String glassType;
final String pictureUrl;
final String instructions;
final List<IngredientModel> ingredientsList;
CocktailModel({
required this.name,
required this.category,
required this.alcoholic,
required this.glassType,
required this.pictureUrl,
required this.instructions,
required this.ingredientsList,
});
CocktailModel.fromJson(Map<String, dynamic> json)
: name = json['strDrink'] ?? '',
category = json['strCategory'] ?? '',
alcoholic = json['strAlcoholic'] ?? '',
glassType = json['strGlass'] ?? '',
pictureUrl = json['strDrinkThumb'] ?? '',
instructions = json['strInstructions'] ?? '',
ingredientsList = _getIngredients(json);
static List<IngredientModel> _getIngredients(
Map<String, dynamic> json,
) {
List<IngredientModel> ingredientsList = [];
for (var i = 1; i <= 16; i++) {
if (json['strIngredient$i'] == null) {
continue;
}
if (json['strMeasure$i'] == null) {
continue;
}
ingredientsList.add(
IngredientModel(
name: json['strIngredient$i'],
mesure: json['strMeasure$i'],
),
);
}
return ingredientsList;
}
}
Unfortunately, in one case I don't know how to use it correctly because I have a certain function (_getIngredients) created that adds more ingredients and measures to me in the right way.
How do I need to write this _getIngredients function so that json_serializable generates the right g.dart file??
I tried to do this in this way, but unfortunately I get null from the api:
import 'package:random_cocktail_app/domain/models/ingredient_model.dart';
import 'package:json_annotation/json_annotation.dart';
part 'cocktail_model.g.dart';
#JsonSerializable()
class CocktailModel {
#JsonKey(name: 'strDrink')
final String name;
#JsonKey(name: 'strCategory')
final String category;
#JsonKey(name: 'strAlcoholic')
final String alcoholic;
#JsonKey(name: 'strGlass')
final String glassType;
#JsonKey(name: 'strDrinkThumb')
final String pictureUrl;
#JsonKey(name: 'strInstructions')
final String instructions;
**#JsonKey(fromJson: _getIngredients)
final List<IngredientModel> ingredientsList;**
CocktailModel({
required this.name,
required this.category,
required this.alcoholic,
required this.glassType,
required this.pictureUrl,
required this.instructions,
required this.ingredientsList,
});
factory CocktailModel.fromJson(Map<String, dynamic> json) =>
_$CocktailModelFromJson(json);
Map<String, dynamic> toJson() => _$CocktailModelToJson(this);
**static List<IngredientModel> _getIngredients(
Map<String, dynamic> json,
) {
List<IngredientModel> ingredientsList = [];
for (var i = 1; i <= 16; i++) {
if (json['strIngredient$i'] == null) {
continue;
}
if (json['strMeasure$i'] == null) {
continue;
}
ingredientsList.add(
IngredientModel(
name: json['strIngredient$i'],
mesure: json['strMeasure$i'],
),
);
}
return ingredientsList;
}**
}
Thank you in advance if anyone can help me solve my problem :)
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());
});
}
}
When i'm parsing my nested json, my media class in my model can be null, I can't check it for null, and i cant map my nested elements
there is my model :
class Product {
Product({
required this.id,
required this.name,
required this.categoryName,
this.media,
});
late final int id;
late final String name;
late final String categoryName;
List<Media>? media;
Product.fromJson(Map<String, dynamic> json){
id = json['id'];
name = json['name'];
categoryName = json['category_name'];
media != null ? (json["media"] as List).map((a) => Media.fromJson(a)).toList() : null;
// media != null ? List.from(json['media']).map((e)=>Media.fromJson(e)).toList() : null;
}
Map<String, dynamic> toJson() {
final data = <String, dynamic>{};
data['id'] = id;
data['name'] = name;
data['category_name'] = categoryName;
data['media'] = media!.map((e)=>e.toJson()).toList();
return data;
}
}
class Media {
Media({
required this.id,
required this.productId,
required this.type,
required this.links,
});
late final int id;
late final int productId;
late final String type;
late final Links links;
Media.fromJson(Map<String, dynamic> json){
id = json['id'];
productId = json['product_id'];
type = json['type'];
links = Links.fromJson(json['links']);
}
Map<String, dynamic> toJson() {
final _data = <String, dynamic>{};
_data['id'] = id;
_data['product_id'] = productId;
_data['type'] = type;
_data['links'] = links.toJson();
return _data;
}
}
when i try to loop it with forEach i got:
error: The argument type 'Media' can't be assigned to the parameter type 'Map<String, dynamic>'. (argument_type_not_assignable at [yiwumart] lib/catalog_screens/catalog_item.dart:72)
final media = productItem.media!.map((e) => Media.fromJson(e)).toList();
Try
Product.fromJson(Map<String, dynamic> json) {
id = json['id'];
name = json['name'];
categoryName = json['category_name'];
media = (json["media"] as List?)?.map((a) => Media.fromJson(a)).toList();
}
the app.quicktype.io cannot convert.
they are converting and skip the digit class name
class ScoreCardModel {
bool? status;
String? msg;
Data? data;
ScoreCardModel({this.status, this.msg, this.data});
ScoreCardModel.fromJson(Map<String, dynamic> json) {
status = json['status'];
msg = json['msg'];
data = json['data'] = Data.fromJson(json['data']);
}
}
class Data {
String? result;
Scorecard? scorecard;
Data({this.result, this.scorecard});
Data.fromJson(Map<String, dynamic> json) {
result = json['result'];
scorecard = json['scorecard'] != null
? Scorecard.fromJson(json['scorecard'])
: null;
}
}
the quicktype cannot work with digits, I don't know why is this. I think they are in the developing stage. I Have the answer. I'm looking for the correct answer.
You can use https://jsontodart.com/, They are just including digit as model(Class) name. But You have to rename the model name like follows,
class 1 {
String score;
String wicket;
String ball;
1({this.score, this.wicket, this.ball});
1.fromJson(Map<String, dynamic> json) {
score = json['score'];
wicket = json['wicket'];
ball = json['ball'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['score'] = this.score;
data['wicket'] = this.wicket;
data['ball'] = this.ball;
return data;
}
}
to
class teamA {
String score;
String wicket;
String ball;
teamA({this.score, this.wicket, this.ball});
teamA.fromJson(Map<String, dynamic> json) {
score = json['score'];
wicket = json['wicket'];
ball = json['ball'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['score'] = this.score;
data['wicket'] = this.wicket;
data['ball'] = this.ball;
return data;
}
}
How do I get access to the "HH" and "mm" using flutter data modeling technique? I've been trying to use it as follows, but it gives an error below.
My data model is currently a simplified version of it.
class Week {
final String label;
final String value;
Week({#required this.label, #required this.value});
factory Week.fromJson(Map<String, dynamic> doc) {
return Week(
label: doc['label'] ?? '',
value: doc['value'] ?? 0,
);
}
}
class IntervalTime {
final String hh;
final String mm;
IntervalTime({#required this.hh, #required this.mm});
factory IntervalTime.fromJson(Map<String, dynamic> doc) {
return IntervalTime(
hh: doc['HH'] ?? '',
mm: doc['mm'] ?? '',
);
}
}
class Diary {
final String message;
final List<Week> weeklyFreq;
final Timestamp annualFreq;
final IntervalTime start;
Diary(
{#required this.message,
#required this.weeklyFreq,
#required this.annualFreq,
#required this.start});
factory Diary.fromFirestore(DocumentSnapshot doc) {
Map data = doc.data;
return Diary(
message: data['message'] ?? '',
weeklyFreq: data['weeklyFreq'].cast<List<Week>>() ?? [],
annualFreq: data['annualFreq'] ?? Timestamp,
start: data['start'].cast<IntervalTime>() ?? '',
);
}
}
And logging is below.
[ERROR:flutter/lib/ui/ui_dart_state.cc(157)] Unhandled Exception: type 'CastList<dynamic, List<Week>>' is not a subtype of type 'List<Week>'
Workaround is below here. Only that matters most is to convert json data again in data declaration. Also, serializing data inside data object is a kind of confusing because there is enough resource to map object inside of object.
class Week {
final String label;
final int value;
Week({#required this.label, #required this.value});
factory Week.fromJson(Map<String, dynamic> data) {
// Map<String, dynamic> data = json.decode(doc);
return Week(
label: data['label'] ?? '',
value: data['value'] ?? 0,
);
}
}
class IntervalTime {
final String hh;
final String mm;
IntervalTime({#required this.hh, #required this.mm});
factory IntervalTime.fromJson(Map data) {
return IntervalTime(
hh: data['HH'] ?? '',
mm: data['mm'] ?? '',
);
}
}
class Diary {
final String message;
final List<Week> weeklyFreq;
final Timestamp annualFreq;
final IntervalTime start;
Diary(
{#required this.message,
#required this.weeklyFreq,
#required this.annualFreq,
#required this.start});
factory Diary.fromFirestore(DocumentSnapshot doc) {
Map data = doc.data;
return Diary(
message: data['message'] ?? '',
weeklyFreq: (data['weeklyFreq'] as List)
?.map((e) => e == null ? null : Week.fromJson(e))
?.toList(), // Workaround
annualFreq: data['annualFreq'] ?? Timestamp,
start: IntervalTime.fromJson(data['start']), // workaround
);
}
}
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;
}
}