I'm creating a flutter app for data visualization.
There will be 3 pages:
Filter page for filter criteria,
List page for visualizing serach result,
Details page.
I will receive a json like this:
data.json
[
{
"name": "Jhon",
"surname": "Walker",
"details": "{\"work\":{\"salary\":\"116\",\"company\":\"evolution\",\"image\":\"http://image.jpg\"},\"address\":{\"street\":\"grand station\",\"city\":\"salt lake\"}}"
},
{
"name": "Alan",
"surname": "Turing",
"details": "{\"work\":{\"salary\":\"116\",\"company\":\"evolution\",\"image\":\"http://image.jpg\"},\"address\":{\"street\":\"grand station\",\"city\":\"salt lake\"}}"
}
]
It could be a really long list.
I have already posted a question here about my code for this app which is not working, here is the discussion
Flutter app error
So now I' m asking a different question. Which is the best model for this json structure?
I need to have direct access to all the fields in the details object. I would like to have the ability to directly get the field salary or company.
I need to have access to the image field which will contain a url for an image to display in the app.
In the linked post I have generated a model using serialization and built_value.
It is not working in the code, I still get the error:
type String is not a subtype of type 'Map<String, dynamic>' in type cast
And no one solutions that I find online seems to work.
I have created another model without nested objects, this is how I made it:
model.dart
class User {
String name;
String surname;
String details;
User({
this.name,
this.surname,
this.details,
});
factory User.fromJson(Map<String, dynamic> json) => User(
name: json["name"],
surname: json["surname"],
details: json["details"],
);
Map<String, dynamic> toJson() => {
"name": name,
"surname": surname,
"details": details,
};
}
With this model I' m able to display the data on a ListView but the details field is one only big string and I have no idea about how to access the fields in the details object.
I can only think to regualr expression but it sounds kind of tricky, and why I should use regualr expression when I have JSON?
Which model is the best for this json? Should I change path?
Do you have any advice for a better data model, or maybe a solution for using the data model correctly from the discussion that I linked?
I' m not asking the same question, I' m just trying to find the right solution for accessing deatils fields using standard json.
Thank you!
Please refer to this link to get the model for your json.
https://app.quicktype.io/
Add your json, select your language and you can have your model.
Try to encode your JSON file:
final data = jsonEncode(file);
final user = User.fromJson(data);
Flutter docs contains some useful samples of JSON encode/decode operations on this page.
Here is the solution with the model I'm currently using:
model.dart
import 'dart:convert';
List<User> userFromJson(String str) =>
List<User>.from(json.decode(str).map((x) => User.fromJson(x)));
String userToJson(List<User> data) =>
json.encode(List<dynamic>.from(data.map((x) => x.toJson())));
class User {
User({
this.name,
this.surname,
this.areaName,
this.details,
});
String name;
String surname;
String areaName;
String details;
factory User.fromJson(Map<String, dynamic> json) => User(
name: json["name"],
surname: json["surname"],
areaName: json["areaName"],
details: json["details"],
);
Map<String, dynamic> toJson() => {
"name": name,
"surname": surname,
"areaName": areaName,
"details": details,
};
}
Details DetailsFromJson(String str) => Details.fromJson(json.decode(str));
String DetailsToJson(Details data) => json.encode(data.toJson());
class Details {
Details({
this.work,
this.address,
});
Work work;
Address address;
factory Details.fromJson(Map<String, dynamic> json) => Details(
work: Work.fromJson(json["work"]),
address: Address.fromJson(json["address"]),
);
Map<String, dynamic> toJson() => {
"work": work.toJson(),
"address": address.toJson(),
};
}
class Address {
Address({
this.street,
this.city,
});
String street;
String city;
factory Address.fromJson(Map<String, dynamic> json) => Address(
street: json["street"],
city: json["city"],
);
Map<String, dynamic> toJson() => {
"street": street,
"city": city,
};
}
class Work {
Work({
this.salary,
this.company,
});
String salary;
String company;
factory Work.fromJson(Map<String, dynamic> json) => Work(
salary: json["salary"],
company: json["company"],
);
Map<String, dynamic> toJson() => {
"salary": salary,
"company": company,
};
}
Thank you for the help!
Related
What I'm trying to do is I have a list of folderTask like this:
List<FolderTask> folderTask = [];
folderTask = [FolderTask(name: name, task: [{task}];
// This is how the task value looks like:
Map<String, dynamic> toJson() => {
"name": name,
"note": note,
"dueDate": dueDate,
"reminderDate": reminderDate,
"reminderTime": reminderTime,
"repeat": repeat,
"path": path,
'isChecked': isChecked
};
So task is a Map List. On my first page I did add folder name into folderTask list and on second page, I want to add the task list inside the folderTask list but it return error of RangeError.
Can someone help me? I'm using Flutter/dart lang to develop this app.
I want to add the task inside the list under the specific folder that has been created by the user on first page.
class FolderTask {
String? name;
List task;
FolderTask({required this.name, required this.task});
factory FolderTask.fromJson(json) {
return FolderTask(name: json['name'], task: json['task']);
}
Map<String, dynamic> toJson() => {"name": name, "task": task};
}
Currently I'm building a self practice eCommerce app using flutter (my back-end is in Laravel). Every time I make a model class, I always come across the issues in fromMap methods.
Currently I cant store my api response to the order (orders and orderItems class is given below).
Error says Unhandled Exception: type 'List' is not a subtype of type 'List' in type cast
here's how I am trying to save the api response
Orders orders = Orders(
List.from(response.data)
.map<OrderItem>((item) => OrderItem.fromMap(item))
.toList(),
);
Orders Class
class Orders {
final List<OrderItem> orders;
Orders(
this.orders,
);
Orders copyWith({
List<OrderItem>? orders,
}) {
return Orders(
orders ?? this.orders,
);
}
Map<String, dynamic> toMap() {
return <String, dynamic>{
'orders': orders.map((x) => x.toMap()).toList(),
};
}
factory Orders.fromMap(Map<String, dynamic> map) {
return Orders(
List<OrderItem>.from(
(map['orders'] as List<int>).map<OrderItem>(
(x) => OrderItem.fromMap(x as Map<String, dynamic>),
),
),
);
}
String toJson() => json.encode(toMap());
factory Orders.fromJson(String source) =>
Orders.fromMap(json.decode(source) as Map<String, dynamic>);
#override
String toString() => 'Orders(orders: $orders)';
#override
bool operator ==(covariant Orders other) {
if (identical(this, other)) return true;
return listEquals(other.orders, orders);
}
#override
int get hashCode => orders.hashCode;
}
OrderItems Class
class OrderItem {
final num id;
final List<num> pid;
OrderItem(
this.id,
this.pid,
);
OrderItem copyWith({
num? id,
List<num>? pid,
}) {
return OrderItem(
id ?? this.id,
pid ?? this.pid,
);
}
Map<String, dynamic> toMap() {
return <String, dynamic>{
'id': id,
'pid': pid,
};
}
factory OrderItem.fromMap(Map<String, dynamic> map) {
return OrderItem(
map['id'] as num,
List<num>.from(
(map['pid'] as List<num>),
),
);
}
String toJson() => json.encode(toMap());
factory OrderItem.fromJson(String source) =>
OrderItem.fromMap(json.decode(source) as Map<String, dynamic>);
#override
String toString() => 'OrderItem(id: $id, pid: $pid)';
#override
bool operator ==(covariant OrderItem other) {
if (identical(this, other)) return true;
return other.id == id && listEquals(other.pid, pid);
}
#override
int get hashCode => id.hashCode ^ pid.hashCode;
}
Here's what the API response looks like
[
{
"id": 1,
"uid": "1",
"pid": [
1,
2
],
"created_at": "2022-07-16T10:34:51.000000Z",
"updated_at": "2022-07-16T10:34:51.000000Z"
},
{
"id": 2,
"uid": "1",
"pid": "[2,3]",
"created_at": "2022-07-16T12:19:15.000000Z",
"updated_at": "2022-07-16T12:19:15.000000Z"
}
]
Typically you don't want to define all these data class methods every time since it can be time consuming and error prone.
I recently created a package which can help you add all this functionality without being too intrusive on your models. You can check it out at: https://pub.dev/packages/mint.
You can also use freezed (https://pub.dev/packages/freezed) which is an excellent package with a proven track record. However, you may have to drastically change your coding style to be compatible with freezed. This is my only issue with it. Mint's primary purpose is to stay out of your way as much as possible. It's also template driven so you can easily modify the templates and add your own functionality as you see fit.
i'm trying to put a response (from an api) on a map but i cant make it work.
Heres the code:
Map<String, dynamic> map = jsonDecode(response);
the "response" word gets red with the error and i cant run the app.
The argument type 'Response<dynamic>' can't be assigned to the parameter type 'String'.dartargument_type_not_assignable
I have also tried putting response.toString() and the app even runs but i get several errors like
E/flutter (10331): #0 _ChunkedJsonParser.fail (dart:convert-patch/convert_patch.dart:1404:5)
E/flutter (10331): #1 _ChunkedJsonParser.parse (dart:convert-patch/convert_patch.dart:934:48)
E/flutter (10331): #2 _parseJson (dart:convert-patch/convert_patch.dart:40:10)
Can someone help me
The response json is:
[
{
"Code": 2025,
"Qty": 4,
"Desc": "SERVIÇO DE ALINHAR EIXOS",
"OsNumber": "3862"
},
{
"Code": 204457,
"Qty": 1,
"Desc": "SERVIÇO REPARO",
"OsNumber": "3862"
}
]
You need to call the method with (response.body) instead of just (response) . So your code should be:
var map = jsonDecode(response.body);
final extractedData = json.decode(response.body) as Map<String, dynamic>;
Then
final List<product> loadedProducts = [];
extractedData.forEach((key, value) {
loadedProducts.add(product(
id: key,
title: value['Code'],
description: value['Qty'],
price: value['Desc'],
imageUrl: value['OsNumber'],
));
product is the Model class of your
{
"Code": 2025,
"Qty": 4,
"Desc": "SERVIÇO DE ALINHAR EIXOS",
"OsNumber": "3862"
},
this Object . So you will get the list of object . so create a Model class for this object and name is product.dart . then the code will work
here is the model for your object
class product {
int code;
int qty;
String desc;
String osNumber;
product({this.code, this.qty, this.desc, this.osNumber});
product.fromJson(Map<String, dynamic> json) {
code = json['Code'];
qty = json['Qty'];
desc = json['Desc'];
osNumber = json['OsNumber'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['Code'] = this.code;
data['Qty'] = this.qty;
data['Desc'] = this.desc;
data['OsNumber'] = this.osNumber;
return data;
}
}
I'm trying to to parse JSON from cloud, the data was received,
I tried so many solution here in stackOverflow but the haven't work to me, I'm just string to get familiar with flutter and dart.
but i got this error:
type '_InternalLinkedHashMap<String, dynamic>' is not a subtype of type 'List<Category>'
here it is my code:
JSON data I received:
{
"totalRowCount": 1,
"pageSize": 100,
"categories": [{
"CategoryName": "Beverages",
"CategoryID": 1
}]
}
Services.dart
import 'package:http/http.dart' as http;
import 'Category.dart';
class Services {
static const String url = 'http://example.com/category';
static Future<List<Category>> getCategories() async {
http.Response response = await http.get(url, headers: {"Accept": "application/json"});
if(response.statusCode == 200){
final category = categoryFromJson(response.body);
return category;
} else{
return List<Category>();
}
}
}
Category.dart
import 'dart:convert';
List<Category> categoryFromJson(String str) => List<Category>.from(json.decode(str));
class Category {
Category({
this.totalRowCount,
this.pageSize,
this.categories,
});
final int totalRowCount;
final int pageSize;
final List<CategoryElement> categories;
factory Category.fromJson(Map<String, dynamic> json){
return Category(
totalRowCount: json["totalRowCount"],
pageSize: json["pageSize"],
categories: List<CategoryElement>.from(json["categories"]),
);
}
Map<String, dynamic> toJson() => {
"totalRowCount": totalRowCount,
"pageSize": pageSize,
"categories": List<dynamic>.from(categories.map((x) => x.toJson())),
};
}
class CategoryElement {
CategoryElement({
this.categoryName,
this.categoryId,
});
final String categoryName;
final int categoryId;
factory CategoryElement.fromJson(Map<String, dynamic> json) => CategoryElement(
categoryName: json["CategoryName"],
categoryId: json["CategoryID"],
);
Map<String, dynamic> toJson() => {
"CategoryName": categoryName,
"CategoryID": categoryId,
};
}
any help
You need to call jsonDecode on response.body to convert the response to a Map.
import 'dart:convert';
final category = Category.fromJson(jsonDecode(response.body));
Remove the following line, it's causing the issue. Decode it right away using the factory instead of creating another function. That function is invalid because List.from accepts an iterable and you're supplying a Map. Also, the json response is not a List.
List<Category> categoryFromJson(String str) => List<Category>.from(json.decode(str));
How it be happend ? i think it happend when i doin json mapping. it said Exception: type 'int' is not a subtype of type 'String'. I've tried using local json assets file but it not make different. Please help me with this problem.
class Product {
int productId;
String productName;
String productPrice;
Product({this.productId, this.productName, this.productPrice});
Product.fromProduct(Product p) {
this.productId = p.productId;
this.productName = p.productName;
this.productPrice = p.productPrice;
}
factory Product.fromJson(Map<String, dynamic> parsedJson) {
return Product(
productId: parsedJson['ID'],
productName: parsedJson['Name'],
productPrice: parsedJson['SellPrice']);
}
}
Future<String> _loadAProductAsset() async {
var res = await http.get(Uri.encodeFull("http://10.0.2.2:9155/product"));
return json.encode(json.decode(res.body)["data"]);
}
List<Product> parseProduct(String myJson) {
final parsed = json.decode(myJson).cast<Map<String, dynamic>>();
return parsed.map<Product>((json) => Product.fromJson(json)).toList();
}
Future<List<Product>> fetchProduct() async {
await wait(1);
String jsonString = await _loadAProductAsset();
return compute(parseProduct, jsonString);
}
Future wait(int s) {
return new Future.delayed(Duration(seconds: s), () => {});
}
this is my json from _loadAProductAsset() function
[
{
"ID": 2,
"CreatedAt": "2020-01-06T03:56:32+07:00",
"UpdatedAt": "2020-01-06T03:56:32+07:00",
"DeletedAt": null,
"Name": "Product A",
"Category": "0",
"Stock": "50",
"StockUnit": "0",
"BuyPrice": "20000",
"SellPrice": "21000",
"SupplierID": "1"
}
]
Solution 1: Instead of explicitly defining the data types as string and ints you could define them as dynamics as follow:
dynamic productId;
dynamic productName;
dynamic productPrice;
What happens here is that you are giving the responsibility to dart of taking care of casting whatever data type that comes in.
Solution 2: Check the structure of incoming JSON by going to the link in your browser window and seeing what is the Data type for each set of data that is coming in. For eg. seeing what is the type of productId. If it is stated as "12" then it must be a string.
After concluding the data type of your JSON items, you could parse the data while deserialising the JSON and defining the variables in the factory constructor(fromProduct in your case). This goes as follows:
Product.fromProduct(Product p) {
this.productId = int.parse(p.productId); // takes in a String and converts it into an int.
this.productName = p.productName;
this.productPrice = p.productPrice;
}
You need to parse the String that comes from JSON into an int:
Product.fromProduct(Product p) {
this.productId = int.parse(p.productId);
this.productName = p.productName;
this.productPrice = p.productPrice;
}
For some reason I had this issue and I already did json.decode(response) earlier but I had to do it again just before doing MyModel.fromJson(response)
So what I typically recommend is
import 'dart:convert';
.
.
.
.
var decodedJson = json.decode(decode);
MyModel model = MyModel.fromJson(decodedJson);
The above worked for me.