Related
I am trying to convert JSON response to dart but over 3 error. I convert the Json from online website https://javiercbk.github.io/json_to_dart/
the errors says : The method 'toJson' isn't defined for the type 'List'.
Try correcting the name to the name of an existing method, or defining a method named 'toJson'.
the code :
class RedZoneModel {
bool? status;
List<Data>? data;
RedZoneModel({this.status, this.data});
RedZoneModel.fromJson(Map<String, dynamic> json) {
status = json['status'];
if (json['data'] != null) {
data = <Data>[];
json['data'].forEach((v) { data!.add(new Data.fromJson(v)); });
}
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['status'] = this.status;
if (this.data != null) {
data['data'] = this.data!.map((v) => v.toJson()).toList();
}
return data;
}
}
class Data {
int? id;
String? areaName;
Geojson? geojson;
String? createdAt;
String? updatedAt;
Data({this.id, this.areaName, this.geojson, this.createdAt, this.updatedAt});
Data.fromJson(Map<String, dynamic> json) {
id = json['id'];
areaName = json['area_name'];
geojson = json['geojson'] != null ? new Geojson.fromJson(json['geojson']) : null;
createdAt = json['created_at'];
updatedAt = json['updated_at'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['id'] = this.id;
data['area_name'] = this.areaName;
if (this.geojson != null) {
data['geojson'] = this.geojson!.toJson();
}
data['created_at'] = this.createdAt;
data['updated_at'] = this.updatedAt;
return data;
}
}
class Geojson {
String? type;
List<List>? coordinates;
Geojson({this.type, this.coordinates});
Geojson.fromJson(Map<String, dynamic> json) {
type = json['type'];
if (json['coordinates'] != null) {
coordinates = <List>[];
json['coordinates'].forEach((v) { coordinates!.add(new List.fromJson(v)); }); // ! Error at List.fromJson(v)
}
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['type'] = this.type;
if (this.coordinates != null) {
data['coordinates'] = this.coordinates!.map((v) => v.toJson()).toList(); // ! Error at v.toJson()
}
return data;
}
}
class Coordinates {
Coordinates({ }); // ! Error at Coordinates({ });
Coordinates.fromJson(Map<String, dynamic> json) {
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
return data;
}
}
and this is the response :
{
"status": true,
"data": [
{
"id": 8,
"area_name": "Surian",
"geojson": {
"type": "Polygon",
"coordinates": [
[
[
-1.2453052,
460.8429751
],
[
-1.2200768,
460.9066456
],
[
-1.2564606,
460.9423423
],
[
-1.2940451,
460.8711205
],
[
-1.2453052,
460.8429751
],
[
-1.2940451,
460.8711205
],
[
-1.2453052,
460.8429751
]
]
]
},
"created_at": "2022-08-29T14:30:10.000000Z",
"updated_at": "2022-12-19T18:30:10.000000Z"
},
{
"id": 13,
"area_name": "test edit",
"geojson": {
"type": "Polygon",
"coordinates": [
[
[
123,
123
],
[
123,
123
],
[
123,
123
],
[
123,
123
],
[
123,
123
]
]
]
},
"created_at": "2022-08-29T15:43:35.000000Z",
"updated_at": "2022-12-20T08:28:00.000000Z"
}
]
}
Anyone have any suggestion?
You shouldn't really use online generators that convert JSON to Dart objects.
Instead, learn about json and serialization. You can use dart packages such as freezed and json_serializable to generate the encoding boilerplate for your JSON response.
Here is sample json to Dart object converter for your json response above:
class Data {
const Data({
required this.id,
required this.area_name,
required this.geojson,
required this.created_at,
required this.updated_at,
});
factory Data.fromJson(Map<String, dynamic> json) => Data(
id: json['id'] as int,
area_name: json['area_name'] as String,
geojson: GeoJson.fromJson(json['geojson'] as Map<String, dynamic>),
created_at: DateTime.parse(json['created_at'] as String),
updated_at: DateTime.parse(json['updated_at'] as String),
);
final int id;
final String area_name;
final GeoJson geojson;
final DateTime created_at;
final DateTime updated_at;
}
class GeoJson {
const GeoJson({
required this.type,
required this.coordinates,
});
factory GeoJson.fromJson(Map<String, dynamic> json) => GeoJson(
type: json['type'] as String,
coordinates: (json['coordinates'] as List<dynamic>)
.map((e) => Polygon.fromJson(e as Map<String, dynamic>))
.toList(),
);
final String type;
final List<Polygon> coordinates;
}
class Polygon {
const Polygon({
required this.lat,
required this.lang,
});
/// Creates a Polygon from Json map
factory Polygon.fromJson(Map<String, dynamic> json) => Polygon(
lat: json['lat'] as num,
lang: json['lang'] as num,
);
final num lat;
final num lang;
}
In a json file, there are two classes which are food and instruction. In the first page, a list of food name with a link will be display. When click on the food name, it will go to the second page where the instruction content related to the food will be display. Here the food -> batters -> batter id is equal to instruction -> category
So my question is how can I practically do or code it in flutter?
Below is the sample of json file:
{
"food": [
{
"id": "0001",
"name": "Cake",
"batters":
{
"batter":
[ { "id": "instruction_1002", "type": "Chocolate" } ]
}
},
{
"id": "0002",
"name": "Raised",
"batters":
{
"batter":
[ { "id": "instruction_1003", "type": "Blueberry" } ]
}
}
],
"instruction": [
{
"category": "instruction_1002",
"content": "abc1234"
},
{
"category": "instruction_1003",
"content": "def56789"
}
]
}
Below are the sample images:
It seems your backend doesn't give you the relationship between those 2 so is up to you to encapuslate them and do the relationship yourself, the easiest way is yo have a Response object that decode your json:
class Response {
Response({
required this.food,
required this.instruction,
});
final List<Food> food;
final List<Instruction> instruction;
factory Response.fromJson(Map<String, dynamic> json) => Response(
food: List<Food>.from(json["food"].map((x) => Food.fromJson(x))),
instruction: List<Instruction>.from(json["instruction"].map((x) => Instruction.fromJson(x))),
);
Map<String, dynamic> toJson() => {
"food": List<dynamic>.from(food.map((x) => x.toJson())),
"instruction": List<dynamic>.from(instruction.map((x) => x.toJson())),
};
}
class FoodResponse {
FoodResponse({
required this.id,
required this.name,
required this.batters,
});
final String id;
final String name;
final Batters batters;
factory FoodResponse.fromJson(Map<String, dynamic> json) => FoodResponse(
id: json["id"],
name: json["name"],
batters: Batters.fromJson(json["batters"]),
);
Map<String, dynamic> toJson() => {
"id": id,
"name": name,
"batters": batters.toJson(),
};
}
class BattersResponse {
BattersResponse({required this.batter});
final List<Batter> batter;
factory BattersResponse.fromJson(Map<String, dynamic> json) => BattersResponse(
batter: List<Batter>.from(json["batter"].map((x) => Batter.fromJson(x))),
);
Map<String, dynamic> toJson() => {
"batter": List<dynamic>.from(batter.map((x) => x.toJson())),
};
}
class BatterResponse {
BatterResponse({
required this.id,
required this.type,
});
final String id;
final String type;
factory BatterResponse.fromJson(Map<String, dynamic> json) => BatterResponse(
id: json["id"],
type: json["type"],
);
Map<String, dynamic> toJson() => {
"id": id,
"type": type,
};
}
class InstructionResponse {
InstructionResponse({
required this.category,
required this.content,
});
final String category;
final String content;
factory InstructionResponse.fromJson(Map<String, dynamic> json) => InstructionResponse(
category: json["category"],
content: json["content"],
);
Map<String, dynamic> toJson() => {
"category": category,
"content": content,
};
}
Now you only need to parse from the response object to your domain (what you actually want)
class Model {
final List<Food> food;
const Model({this.food = const <Food>[]});
factory Model.fromResponse(Response response) {
final foodList = response.food.toList();
final instructionsResponse = response.instruction.toList();
final List<Food> items = [];
for (int f = 0; f < foodList.length; f++) {
final List<Instruction> instructions = [];
final food = foodList[f];
food.batters.batter.forEach((b) {
for (int i = 0; i < instructionsResponse.length; i++) {
final singleInstruction = instructionsResponse[i];
if (singleInstruction.category == b.id) {
final Instruction instruction = Instruction(
id: b.id,
content: singleInstruction.content,
ingredient: b.type,
);
instructions.add(instruction);
}
}
items.add(Food(id: food.id, name: food.name, instructions: instructions));
});
}
return Model(food: items);
}
}
class Food {
final String id;
final String name;
final List<Instruction> instructions;
const Food({
required this.id,
required this.name,
this.instructions = const <Instruction>[],
});
}
class Instruction {
final String id;
final String content;
final String ingredient;
const Instruction({
required this.id,
required this.content,
required this.ingredient,
});
}
Now you can use Model in your UI which have both food and their instructions related. This is maybe not the best approach for simple apps, but when your app starts to grow it will be simplier to mantain your Model if your backend changess the format of the json. At the end all this should be in an state management that simplifies the relation between your UI and your repositories (backend)
what do you want, that's very easy
just define 2 classes and decode the JSON file and after that, store that into the state management, and finally use it
The quickest way to do it from a known JSON is to use app.quicktype.io, paste your json, select dart and have it translate into classes for you!
I want to deserialize some JSON data that contains a list of article information
{
"data": [
{
"id": 1,
"title": "First article",
"createdDate": "2022-03-20T11:46:00",
"content": "Markdown content",
"author": 1,
"category": 1
},
{
"id": 2,
"title": "Second article",
"createdDate": "2022-03-20T11:46:00",
"content": "Markdown content",
"author": 1,
"category": 1
}
]
}
No matter what the request is, the top level will have a key called data
So, I created a generic class called Entry
import 'package:json_annotation/json_annotation.dart';
part 'Entry.g.dart';
#JsonSerializable(genericArgumentFactories: true)
class Entry<TData> {
Entry(this.data);
TData data;
factory Entry.fromJson(Map<String, dynamic> json,TData Function(dynamic json) fromJsonTData) => _$EntryFromJson(json,fromJsonTData);
Map<String, dynamic> toJson(Object? Function(TData value) toJsonTData) => _$EntryToJson(this,toJsonTData);
}
And for an article, I created a class call NovelData
import 'dart:convert';
import 'dart:core';
import 'package:json_annotation/json_annotation.dart';
import 'Entry.dart';
part 'NovelData.g.dart';
#JsonSerializable(genericArgumentFactories: true)
class NovelData {
NovelData(this.id, this.title, this.createdDate, this.content, this.author, this.category);
int id;
String title;
String createdDate;
String content;
int author;
int category;
factory NovelData.fromJson(Map<String, dynamic> json) =>
_$NovelDataFromJson(json);
Map<String, dynamic> toJson() => _$NovelDataToJson(this);
}
Now, if I want to use the type like Entry<List<Novel>>> to deserialize the above JSON data, what should I do?
You can access them through the full path to the data.
Full path to your data: Map => key data => Array => Array index => Map
{}.data.[].0.{}
It only takes one class.
import 'package:fast_json/fast_json_selector.dart' as parser;
void main() async {
final path = '{}.data.[].0.{}';
final pathLevel = path.split('.').length;
final items = <Novel>[];
void select(parser.JsonSelectorEvent event) {
if (event.levels.length == pathLevel) {
if (event.levels.join('.') == path) {
final item = Novel.fromJson(event.lastValue as Map);
items.add(item);
event.lastValue = null;
}
}
}
parser.parse(_json, select: select);
print(items.join('\n'));
}
final _json = '''
{
"data": [
{
"id": 1,
"title": "First article",
"createdDate": "2022-03-20T11:46:00",
"content": "Markdown content",
"author": 1,
"category": 1
},
{
"id": 2,
"title": "Second article",
"createdDate": "2022-03-20T11:46:00",
"content": "Markdown content",
"author": 1,
"category": 1
}
]
}''';
class Novel {
final int id;
final String title;
Novel({required this.id, required this.title});
#override
String toString() {
return title;
}
static Novel fromJson(Map json) {
return Novel(
id: json['id'] as int,
title: json['title'] as String,
);
}
}
Output:
First article
Second article
You can get the data before adding it to the list. The result is no different. Just a different path to the data.
void main() async {
final path = '{}.data.[].0';
final pathLevel = path.split('.').length;
final items = <Novel>[];
void select(parser.JsonSelectorEvent event) {
if (event.levels.length == pathLevel) {
if (event.levels.join('.') == path) {
final item = Novel.fromJson(event.lastValue as Map);
items.add(item);
event.lastValue = null;
}
}
}
parser.parse(_json, select: select);
print(items.join('\n'));
}
This event follows the object creation event (at a lower event level):
JsonHandlerEvent.endObject => JsonHandlerEvent.element
You can get the data after adding it to the list. But it won't be as efficient.
void main() async {
final path = '{}.data.[]';
final pathLevel = path.split('.').length;
final items = <Novel>[];
void select(parser.JsonSelectorEvent event) {
if (event.levels.length == pathLevel) {
if (event.levels.join('.') == path) {
final list = event.lastValue as List;
items.addAll(list.map((e) => Novel.fromJson(e as Map)));
list.clear();
}
}
}
parser.parse(_json, select: select);
print(items.join('\n'));
}
JsonHandlerEvent.endObject => JsonHandlerEvent.element => JsonHandlerEvent.endArray
Or even from property data. Very inefficient because all data is stored in memory.
void main() async {
final path = '{}.data';
final pathLevel = path.split('.').length;
final items = <Novel>[];
void select(parser.JsonSelectorEvent event) {
if (event.levels.length == pathLevel) {
if (event.levels.join('.') == path) {
final list = event.lastValue as List;
items.addAll(list.map((e) => Novel.fromJson(e as Map)));
event.lastValue = null;
}
}
}
parser.parse(_json, select: select);
print(items.join('\n'));
}
JsonHandlerEvent.endObject => JsonHandlerEvent.element => JsonHandlerEvent.endArray => JsonHandlerEvent.endKey
I won't even write about the last level. There is no point in such an inefficient way. However, and in the previous one, too.
JsonHandlerEvent.endObject => JsonHandlerEvent.element => JsonHandlerEvent.endArray => JsonHandlerEvent.endKey => JsonHandlerEvent.endObject
There is a website which automatically generates all needed code from json. Here is example:
// To parse this JSON data, do
//
// final entry = entryFromJson(jsonString);
import 'dart:convert';
Entry entryFromJson(String str) => Entry.fromJson(json.decode(str));
String entryToJson(Entry data) => json.encode(data.toJson());
class Entry {
Entry({
this.data,
});
List<Datum> data;
factory Entry.fromJson(Map<String, dynamic> json) => Entry(
data: List<Datum>.from(json["data"].map((x) => Datum.fromJson(x))),
);
Map<String, dynamic> toJson() => {
"data": List<dynamic>.from(data.map((x) => x.toJson())),
};
}
class Datum {
Datum({
this.id,
this.title,
this.createdDate,
this.content,
this.author,
this.category,
});
int id;
String title;
DateTime createdDate;
String content;
int author;
int category;
factory Datum.fromJson(Map<String, dynamic> json) => Datum(
id: json["id"],
title: json["title"],
createdDate: DateTime.parse(json["createdDate"]),
content: json["content"],
author: json["author"],
category: json["category"],
);
Map<String, dynamic> toJson() => {
"id": id,
"title": title,
"createdDate": createdDate.toIso8601String(),
"content": content,
"author": author,
"category": category,
};
}
you can try my jsonize package, it will handle any of your TData classes wherever they are in your Entry data list
import 'package:jsonize/jsonize.dart';
abstract class TData {}
class Entry implements Jsonizable {
Entry({
required this.data,
});
factory Entry.empty() => Entry(data: []);
List<TData> data;
#override
String get jsonClassCode => "Entry";
#override
Entry fromJson(json) => Entry(data: List<TData>.from(json["data"]));
#override
Map<String, dynamic> toJson() => {"data": data};
}
class NovelData extends TData implements Jsonizable {
NovelData({
required this.id,
required this.title,
required this.createdDate,
required this.content,
required this.author,
required this.category,
});
factory NovelData.empty() => NovelData(
id: 0,
title: "",
createdDate: DateTime(0),
content: "",
author: 0,
category: 0);
int id;
String title;
DateTime createdDate;
String content;
int author;
int category;
#override
String get jsonClassCode => "NovelData";
#override
NovelData fromJson(json) => NovelData(
id: json["id"],
title: json["title"],
createdDate: json["createdDate"],
content: json["content"],
author: json["author"],
category: json["category"],
);
#override
Map<String, dynamic> toJson() => {
"id": id,
"title": title,
"createdDate": createdDate,
"content": content,
"author": author,
"category": category,
};
}
main() {
Jsonize.registerClass(Entry.empty());
Jsonize.registerClass(NovelData.empty());
NovelData novel1 = NovelData(
id: 1,
title: "First article",
createdDate: DateTime.now(),
content: "Markdown content",
author: 1,
category: 1);
NovelData novel2 = NovelData(
id: 2,
title: "Second article",
createdDate: DateTime.now(),
content: "Markdown content",
author: 1,
category: 1);
Entry myEntry = Entry(data: [novel1, novel2]);
String myEntryJson = Jsonize.toJson(myEntry);
print(myEntryJson);
Entry entryBackToLife = Jsonize.fromJson(myEntryJson);
print(entryBackToLife);
}
jsonize can do more like handling enums. In your case benefits are:
DateTime serialization is handled by jsonize, you don not need to transform it
You can derive new classes from TData and put them into your Entry, jsonize will handle them and automatically transform it back to the original class
This is probably going to be a stupid question but I have a problem to understand how fromJson and toJson works.
In this case if I use a List I do like that:
class DataPerDaysInfo{
List<Product>? products;
DataPerDaysInfo({required this.products});
factory DataPerDaysInfo.fromJson(Map<String, dynamic> json) => DataPerDaysInfo(
products : json["products"] == null ? null: List<Product>.from(json["products"].map((x) => Product.fromJson(x))));
Map<String, dynamic> toJson() =>
{
"products": products == null
? null
: List<dynamic>.from(products!.map((x) => x.toJson())),
};
}
but if I need to use a map how can I do it?
class DataPerDaysInfo{
Map<String, List<Product>> mapper;
DataPerDaysInfo({required this.mapper});
}
Without having a json string given. It is hard to guess. But maybe this implementation helps you a little:
import 'dart:convert';
void main() {
var dpdi = DataPerDaysInfo.fromJson(jsonDecode(json));
print(dpdi.toJson());
}
final String json = '''
{
"catalog1": [
{"id": 1, "name": "first"},
{"id": 2, "name": "second"}
],
"catalog2": [
{"id": 1, "name": "first"},
{"id": 2, "name": "second"}
]
}
''';
class DataPerDaysInfo {
Map<String, List<Product>> mapper = {};
DataPerDaysInfo.fromJson(Map<String, dynamic> json) {
json.forEach((key, value) => mapper[key] = value
.map((val) => Product.fromJson(val))
.cast<Product>()
.toList(growable: false));
}
String toJson() {
return jsonEncode(mapper);
}
}
class Product {
final int id;
final String name;
Product.fromJson(Map<String, dynamic> json)
: id = json['id'],
name = json['name'];
#override
String toString() {
return 'id: $id, name: $name';
}
Map<String, dynamic> toJson() {
return {'id': id, 'name': name};
}
}
I have a json response where there is a list that contains a list, then there is a map in it, I tried to parse it but it failed, what kind of error did I do? Previously I used this method to parse the map that was in the list and it worked
response json
{
"status": "success",
"data": [
{
"id_category": 1,
"category_slug": "cc",
"category_name": "Credit Card",
"data_payment": [
{
"payment_slug": "cc",
"payment_name": "Credit Card",
"payment_logo": "https://cdn.xx.id/assets_midtrans/cc.png"
}
]
}
],
"message": "Success Get Data"
}
mycode
class PaymentMethodListModel {
final String status, message;
final List<_Data> data;
PaymentMethodListModel({
this.status,
this.message,
this.data,
});
factory PaymentMethodListModel.fromJson(Map<String, dynamic> x) {
var list = x['data'] as List;
print(list.runtimeType);
List<_Data> sd = list.map((i) => _Data.fromJson(i)).toList();
return PaymentMethodListModel(
status: x['status'],
data: sd,
message: x['message'],
);
}
}
class _Data {
final String categorySlug, categoryName;
final List<DataPayment> dataPayment;
_Data({
this.categorySlug,
this.categoryName,
this.dataPayment,
});
factory _Data.fromJson(Map<String, dynamic> obj) {
var list = obj['data_payment'] as List;
List<DataPayment> dataPaymentList = list.map((i) => DataPayment.fromJson(i)).toList();
return _Data(
categorySlug: obj['category_slug'],
categoryName: obj['category_name'],
dataPayment: dataPaymentList,
);
}
}
class DataPayment {
final String paymentSlug, paymentName, paymentLogo;
DataPayment({
this.paymentSlug,
this.paymentName,
this.paymentLogo,
});
factory DataPayment.fromJson(Map<String, dynamic> x) => DataPayment(
paymentSlug: x['payment_slug'],
paymentName: x['payment_name'],
paymentLogo: x['payment_logo'],
);
}
error message
The method 'map' was called on null.
Receiver: null
Tried calling: map(Closure: (dynamic) => DataPayment)
Try below code
var paymentListModel = PaymentListModel.fromJson(json.decode(str));
class PaymentListModel {
PaymentListModel({
this.status,
this.data,
this.message,
});
String status;
List<Datum> data;
String message;
factory PaymentListModel.fromJson(Map<String, dynamic> json) => PaymentListModel(
status: json["status"],
data: List<Datum>.from(json["data"].map((x) => Datum.fromJson(x))),
message: json["message"],
);
Map<String, dynamic> toJson() => {
"status": status,
"data": List<dynamic>.from(data.map((x) => x.toJson())),
"message": message,
};
}
class Datum {
Datum({
this.idCategory,
this.categorySlug,
this.categoryName,
this.dataPayment,
});
int idCategory;
String categorySlug;
String categoryName;
List<DataPayment> dataPayment;
factory Datum.fromJson(Map<String, dynamic> json) => Datum(
idCategory: json["id_category"],
categorySlug: json["category_slug"],
categoryName: json["category_name"],
dataPayment: List<DataPayment>.from(json["data_payment"].map((x) => DataPayment.fromJson(x))),
);
Map<String, dynamic> toJson() => {
"id_category": idCategory,
"category_slug": categorySlug,
"category_name": categoryName,
"data_payment": List<dynamic>.from(dataPayment.map((x) => x.toJson())),
};
}
class DataPayment {
DataPayment({
this.paymentSlug,
this.paymentName,
this.paymentLogo,
});
String paymentSlug;
String paymentName;
String paymentLogo;
factory DataPayment.fromJson(Map<String, dynamic> json) => DataPayment(
paymentSlug: json["payment_slug"],
paymentName: json["payment_name"],
paymentLogo: json["payment_logo"],
);
Map<String, dynamic> toJson() => {
"payment_slug": paymentSlug,
"payment_name": paymentName,
"payment_logo": paymentLogo,
};
}