Flutter How to insert nested JSON array into different SQLite tables - json

I hope I could explain clearly my need, What I have is an API that I connect to and recive a JSON from it.
The Json is nested into a list and an object that have a 2ed tier list.
What i want is to insert this Josn into my local DB (Using SQLite for Flutter).
The point I reached is the inserting of the 1st tier of the json data, but could not insert the data that exist as a list in the 2ed tier. Although I serilize it but the list after when i try to insert, have no mappnig from a list form to the database table fields.
Its like I have [Class User] and [Class Company] A user have many companies rolled in.
When serilaizing the data from the JSON I have the data come to the User Class then nesting to the Companies Class and do the serilaizng for the 2ed tier of the json object then return.
The next step is to insert the data into DB but the data I have in my last object is something like this:
"username" : userName
"company" : CompanyList --- This is a list and cant be inserted into the DB directly in a nested way. As the companylist have multyble fields. and more than one records ex: two companies.
I belive there would be A map to do this in reverse so i could insert it to the DB (I have it in my Company Class) but i cant call it from the place where i try to insert the data into DB due of the differnce in the Classes instances
The instance in my hand is a User Type instance [The main Class between the two], while the companyList if wanted to map it needs a Company insance type.
I can provide code or explain more, any help or idea about the approach may help. Thanks a lot!
#EDIT
This where i try to insert the data into DB
The first inserting is fine but the 2ed one is not.
Future<int> createUserPermissions(UserPermissions userPermissions, ComListPermissions comListPermissions) async {
final db = await dbProvider.database;
var result = db.insert(userPermissionsDBTable, userPermissions.mapToDB());
db.insert(userCompaniesTableDB, comListPermissions.toJson());
return result;
}
User Class
class UserPermissions {
final String userName;
final List<ComListPermissions> comLists;
final bool active;
final String groupName;
final int companyId;
final String permissions;
final ComListPermissions company;
UserPermissions({this.company, this.groupName, this.companyId, this.permissions, this.userName, this.active, this.comLists});
factory UserPermissions.mapFromJsonToDB(Map<String, dynamic> data) {
if (data['comLists'] != null) {
var companyObjJson = data['comLists'] as List;
List<ComListPermissions> _companies = companyObjJson
.map((companyJson) => ComListPermissions.fromJson(companyJson))
.toList();
return UserPermissions(
userName: data['userName'],
active: data['active'],
comLists: _companies,
);
} else {
return UserPermissions(
userName: data['userName'],
active: data['active'],
);
}
}
Map<String, dynamic> mapToDB() => {
"userName": userName,
// "comLists": comLists,
"active": active,
};
}
Comapny Class
class ComListPermissions {
ComListPermissions({
this.groupName,
this.companyId,
this.permissions,
});
String groupName;
int companyId;
String permissions;
//List<String> permissions;
factory ComListPermissions.fromJson(Map<String, dynamic> json) {
if (json['permissions'] != null) {
var permissionsObjJson = json['permissions'] as List;
String s = permissionsObjJson.toString();
return ComListPermissions(
groupName: json["groupName"],
companyId: json["companyID"],
permissions: s,
);
} else {
return ComListPermissions(
groupName: json["groupName"],
companyId: json["companyID"],
);
}
}
Map<String, dynamic> toJson() => {
"groupName": groupName,
"companyID": companyId,
"permissions": permissions,
//"permissions": List<dynamic>.from(permissions.map((x) => x)),
};
}

I Just added a for loop and sent index every time i needed to insert a new recırd into my DB
Future<int> createUserPermissions(UserPermissions userPermissions) async {
final db = await dbProvider.database;
var result = db.insert(userPermissionsDBTable, userPermissions.mapToDB());
int index = userPermissions.comLists.length;
for (int i = 0; i < index; i++) {
db.insert(userCompaniesTableDB, userPermissions.toDatabaseForCompany(i));
}
return result;
}
Map<String, dynamic> toDatabaseForCompany(int index) => {
"companyID": comLists[index].companyId,
"groupName": comLists[index].groupName,
"permissions": comLists[index].permissions,
};

Related

Return multiple Objects from json with dart

In a Flutter app I'm writing, I'm a beginner, I want to return 3 Objects from an http request. The http request works fine and returns data like:
{"one":{"fname":"Wyn","lname":"Owen","updated":1648076276673,"uid":"contact's uid","email":"wyn#wyn.com"},
"two":{"updated":1648076276673,"uid":"contact's uid","fname":"Roli","email":"roli#roli.com","lname":"Spider"},
"three":{"lname":"Poodle","email":"bill#bill.com","updated":1648076276673,"fname":"Bill","uid":"contact's uid"},
"user":{"name":null,"premium":false,"reg_id":"123456","created":1648076276673,"ads":true},
"channels":{"whatsapp":false,"email":true,"sms":false,"video":false,"app_msg":true},"shorts":{"expire":null,"code":"short_code"}}
It returns 5 KV pairs. The keys will always remain the same. All I'm interested are the 3 KVs: one, two and three. I want to create an object for each KV pair and call them one, two and three. I created the following model: Object Contacts contains 3 Person Objects:
class Contacts {
Contacts({
required this.one,
required this.two,
required this.three,
});
Person one;
Person two;
Person three;
factory Contacts.fromJson(Map<String, dynamic> json) => Contacts(
one: json["one"],
two: json["two"],
three: json["three"],
);
Map<String, dynamic> toJson() => {
"one": one.toJson(),
"two": two.toJson(),
"three": three.toJson(),
};
}
class Person {
Person({
required this.uid,
required this.updated,
required this.email,
required this.lname,
required this.fname,
});
String uid;
int updated;
String email;
String lname;
String fname;
factory Person.fromJson(Map<String, dynamic> json) => Person(
uid: json["uid"],
updated: json["updated"],
email: json["email"],
lname: json["lname"],
fname: json["fname"],
);
Map<String, dynamic> toJson() => {
"uid": uid,
"updated": updated,
"email": email,
"lname": lname,
"fname": fname,
};
}
This is the class I wrote:
class ContactsService {
Future<List<Person>> fetchPersons(String uid) async {
http.Response response =
await http.get(Uri.parse("$PersonURL?uid=$uid"));
if (response.statusCode == 200) {
Map ContactData = jsonDecode(response.body);
Person one = Person.fromJson(ContactData["one"]);
Person two = Person.fromJson(ContactData["two"]);
Person three = Person.fromJson(ContactData["three"]);
List Persons = [];
Persons.add(one);
Persons.add(two);
Persons.add(three);
return Persons;
} else {
throw Exception("Something has gone wrong, ${response.statusCode}");
}
}
}
To get the objects in my Scaffold I would put
future: ContactsService.fetchPersons()
I want to be able to refer to one.fname, two.email etc I guess I'm missing something within fetchPersons(). So what is missing?
TIA
You can solve it by changing
Map ContactData = jsonDecode(response.body);
to
final contacts = Contacts.fromJson(jsonDecode(a));
and
Person one = Person.fromJson(ContactData["one"]);
Person two = Person.fromJson(ContactData["two"]);
Person three = Person.fromJson(ContactData["three"]);
to
final one = contacts.one;
final two = contacts.two;
final three = contacts.three;
And as a supplement, serialization codes will be unnecessary if you use JsonSerializable provided by Google.

Flutter - creating a search delegate data 'getter' model, from CSV data

At the moment, I am building a Flutter app to try and develop my skills, which includes a search delegate function. At the moment, my search delegate works great, when the data is imported from json format, which looks like this:
[
{
name: John,
age: 22,
height: 1.85,
searchTerm: John 22 1.85,
},
{
name: Alice,
age: 24,
height: 1.90,
searchTerm: Alice 24 1.90,
},
{
name: Bruce,
age: 35,
height: 1.76,
searchTerm: Bruce 35 1.76,
}
]
To allow my search delegate to work, I built a getter model that looks like this.
import 'dart:convert';
import 'package:flutter/services.dart';
class People {
final String name;
final double age;
final double height;
final String searchTerm;
People({
required this.name,
required this.age,
required this.height,
required this.searchTerm,
});
static People fromJson(Map<String, dynamic> json) => People(
name: json['name'],
age: json['age'],
height: json['height'],
searchTerm: json['searchTerm'],
);
}
class PeopleGetter {
static Future<List<People>> getPeopleSuggestions(String query) async {
try {
final String response =
await rootBundle.loadString('assets/data/people.json');
final List people = json.decode(response);
return people
.map((json) => People.fromJson(json))
.where((people) {
final searchTermLower = people.searchTerm.toLowerCase();
final queryLower = query.toLowerCase();
return searchTermLower.contains(queryLower);
}).toList();
} catch (e) {
print(e);
}
throw '';
}
}
This actually does work perfectly with my search delegate. However, since storing the data in a CSV file can significantly reduce the overall data size, and therefore the overall app size, I want to replace the json data with CSV data. I've tried modifying my 'getter' function from json data, to use CSV data (using the CSV package, from pub.dev), which you can see here:
import 'package:csv/csv.dart';
import 'package:flutter/services.dart';
class People {
final String name;
final double age;
final double height;
final String searchTerm;
People({
required this.name,
required this.age,
required this.height,
required this.searchTerm,
});
static People fromCSV(Map<String, dynamic> csv) => People(
name: csv['name'],
age: csv['age'],
height: csv['height'],
searchTerm: csv['searchTerm'],
);
}
class PeopleGetter {
static Future<List<People>> getPeopleSuggestions(String query) async {
try {
final String response =
await rootBundle.loadString('assets/data/people.csv');
final List people = CsvToListConverter(response);
return people
.map((csv) => People.fromCSV(csv))
.where((people) {
final searchTermLower = people.searchTerm.toLowerCase();
final queryLower = query.toLowerCase();
return searchTermLower.contains(queryLower);
}).toList();
} catch (e) {
print(e);
}
throw '';
}
}
Unfortunately, when I use this version within the search delegate, I get the error:
'type 'List<dynamic>' is not a subtype of type 'Map<String, dynamic>'.
I feel like I'll just have made a very simple error somewhere but at the moment, I just can't spot it. I would really appreciate any help correcting my code, to allow me to search with CSV data, instead of JSON data. Thanks!
It seems that your data types are not corresponding anymore:
You provide a List<dynamic> to a function that expects a Map<>
people.map((csv) => People.fromCSV(csv))
static People fromCSV(Map<String, dynamic> csv) => People(
name: csv['name'],
age: csv['age'],
height: csv['height'],
searchTerm: csv['searchTerm'],
);
Could you try rewriting your function from CSV to adapt to the new incoming data ? I'm not sure what is the data you get but maybe something like that ?
static People fromCSV(List<dynamic> csv) => People(
name: csv[0] ?? "",
age: csv[1] ?? -1,
height: csv[2] ?? -1,
searchTerm: csv[3] ?? "",
);
EDIT (fixing the few remaining bugs)
Updated CSV package version
Correctly retrieve the double from the csv
Set the delimiter parameter to separate two people in the CSV
class People {
final String name;
final double age;
final double height;
final String searchTerm;
People({
required this.name,
required this.age,
required this.height,
required this.searchTerm,
});
static People fromCSV(List<dynamic> csv) => People(
name: csv[0] ?? '',
age: double.parse(csv[1].toString()),
height: double.parse(csv[2].toString()),
searchTerm: csv[3] ?? '',
);
}
class PeopleGetter {
static Future<List<People>> getPeopleSuggestions(String query) async {
try {
final String response = await rootBundle.loadString('assets/data.csv');
final List<List<dynamic>> people =
CsvToListConverter().convert(response, eol: "\n");
return people.map((csv) => People.fromCSV(csv)).where((people) {
final searchTermLower = people.searchTerm.toLowerCase();
final queryLower = query.toLowerCase();
return searchTermLower.contains(queryLower);
}).toList();
} catch (e) {
print(e);
}
throw '';
}
}

Assign value to a variable from list object converted from JSON in Flutter

I'm really new to flutter. I tried searching for this question and although I have found a solution but not all my problems are solved, as most of it just use return.
I have a JSON that i get from API calling here:
{
"error": false,
"message": "LOGIN_SUCCESS",
"user": {
"id": 1219,
"email": "john#example.com"
"name": "John Doe",
"category": 1,
"branch": 1004,
"lastlogin": "2020-12-04 03:12:43"
}
}
I already create the class for user as below
class User {
int id;
String name;
String email;
String category;
String branch;
String lastLogin;
User({
this.id,
this.name,
this.email,
this.category,
this.branch,
this.lastLogin
});
factory User.fromJson(Map<String, dynamic> datauser){
return User(
id: datauser['id'],
name: datauser['name'],
email: datauser['email'],
category: datauser['category'],
branch: datauser['branch'],
lastLogin: datauser['lastlogin']
);
}
}
and a result class as below..
class Result {
String message;
User user;
Result({
this.message,
this.user
});
factory Result.fromJson(Map<String, dynamic> resultData){
return Result(
message: resultData['message'],
user: User.fromJson(resultData['user'])
);
}
}
now here comes my problem as i don't know how to move forward from this point
login() async {
List<User> users;
final response = await http.post("myUrlWithAPIcalls",
body: {"email": email, "password": password});
final data = jsonDecode(response.body);
var rest = data['user'] as List;
users = rest.map<User>((json) => User.fromJson(json)).toList();
}
so the question is, how can i assign the value i get from the JSON that has converted into list into a variable?
now in example if it was only a simple JSON object, i could do it like this..
final data = jsonDecode(response.body);
int id = data['id'];
String name = data['name'];
String email = data['email'];
String category = data['category'];
String branch = data['branch'];
but how can i do that in a list object?
as of anyone was wondering why do i do like this, i was trying to save the data into a sharedpref class that i copied from someone else code.
Shared preferences are not meant to store objects. Use something like sqflite to persist objects (official cookbook here).
I don't understand why your JSON shows one user's data, but the login() function seems to decode a list of users.
I'm guessing that's what you want :
login() async {
final response = await http.post("myUrlWithAPIcalls",
body: {"email": email, "password": password});
final data = jsonDecode(response.body);
var user = User.fromJson(data['user']); // the variable you want
}
You don't say where that login() function is, or what you want to do with that User object. FYI, an essential part of Flutter is state management.

Flutter converting Nested Object from Json returns null

I have a Nested Object like this just a bit bigger:
"name": "String",
"exercise": [
{
"index": 1,
}
],
"pause": [
{"index":2},
]
I convert the exercise and pause to a Json String and save them in a column in SQFLite.
The problem
When I read the Data everything works fine including the List (not nested) but both list's of nested Object are empty when I read a value of the nested object it gives an error.
item.exercise[0].index.toString()
Valid Value range is empty: 0
When I read only item.exercise.toString() it returns []. Without != null ? [...] : List<Exercise>() it also throws an error
Data I get from my Database (shortened)
List of:
[{name: number 1, id: 56, exercise: [{"index":1,"weightGoal":[15,16,17]}, {"index":3,"weightGoal":[15,16,17]}], pause: [{"index":2}]},{"index":4}]}]
What I do with it
Here I try to go through the list and convert it into a List of PlanModel:
List<PlanModel> list =
res.isNotEmpty ? res.map((c) => PlanModel.fromJson(c)).toList() : [];
return list;
Full model
PlanModel planModelFromJson(String str) => PlanModel.fromJson(json.decode(str));
String planModelToJson(PlanModel data) => json.encode(data.toJson());
class PlanModel {
PlanModel({
this.name,
this.id,
this.workoutDays,
this.pastId,
this.timesDone,
this.exercise,
this.pause,
});
String name;
int id;
List<String> workoutDays;
int pastId;
int timesDone;
List<Exercise> exercise;
List<Pause> pause;
factory PlanModel.fromJson(Map<String, dynamic> json) => PlanModel(
name: json["name"],
id: json["id"],
workoutDays: List<String>.from(jsonDecode(json["workoutDays"])),
pastId: json["pastId"],
timesDone: json["timesDone"],
exercise: json["Exercise"] != null ? new List<Exercise>.from(json["Exercise"].map((x) => Exercise.fromJson(x))): List<Exercise>(),
pause: json["Pause"] != null ? new List<Pause>.from(json["Pause"].map((x) => Pause.fromJson(x))): List<Pause>(),
);
Map<String, dynamic> toJson() => {
"name": name,
"id": id,
"workoutDays": List<dynamic>.from(workoutDays.map((x) => x)),
"pastId": pastId,
"timesDone": timesDone,
"Exercise": List<dynamic>.from(exercise.map((x) => x.toJson())),
"Pause": List<dynamic>.from(pause.map((x) => x.toJson())),
};
}
class Exercise {
Exercise({
this.index,
this.name,
this.goal,
this.repGoal,
this.weightGoal,
this.timeGoal,
this.setGoal,
});
int index;
String name;
int goal;
int repGoal;
List<int> weightGoal;
int timeGoal;
List<String> setGoal;
Exercise.fromJson(dynamic json) {
// anything that is wrapped around with this [] in json is converted as list
// anything that is wrapped around with this {} is map
index = json["index"];
name = json["name"];
goal = json["goal"];
repGoal = json["repGoal"];
weightGoal = json["weightGoal"] != null ? json["weightGoal"].cast<int>() : [];
timeGoal = json["timeGoal"];
setGoal = json["setGoal"] != null ? json["setGoal"].cast<String>() : [];
}
Map<String, dynamic> toJson() => {
"index": index,
"name": name,
"goal": goal,
"repGoal": repGoal,
"weightGoal": List<dynamic>.from(weightGoal.map((x) => x)),
"timeGoal": timeGoal,
"setGoal": List<dynamic>.from(setGoal.map((x) => x)),
};
}
class Pause {
Pause({
this.index,
this.timeInMilSec,
});
int index;
int timeInMilSec;
factory Pause.fromJson(Map<String, dynamic> json) => Pause(
index: json["index"],
timeInMilSec: json["timeInMilSec"],
);
Map<String, dynamic> toJson() => {
"index": index,
"timeInMilSec": timeInMilSec,
};
}
Read this first.
You need to tweek this code a little to work for you but the idea is that;
also read comment in the code.
if json string comes with [] those around, json.decode will decode it as List<Map>.
if it comes with {} this json.decode will decode it as Map.
note: be careful while using generics on json.decode I reccommend not to.
data inside the jsonString does not really corresponds with the values inside the fromJson function. json string which you have provided was not really good. so I think you will understand how to manipulate it for your needs.
also main constructor Exercise you can use for initial data.
import 'dart:convert';
class Exercise{
Exercise({this.index,
this.name,
this.repGoal,
this.weightGoal,
this.setGoal});
String index;
String name;
String repGoal;
String weightGoal;
String setGoal;
Exercise.fromJson(dynamic json) :
// anything that is wrapped around with this [] in json is converted as list
// anything that is wrapped around with this {} is map
index = json["exercise"][0]["index"].toString(),
name = json["name"].toString(),
repGoal = json["repGoal"].toString(),
weightGoal = json["weightGoal"].toString(),
setGoal = json["setGoal"].toString();
}
void main(){
String jsonString = '{name: number 1, id: 56, exercise: [{"index":1,"weightGoal":[15,16,17], pause: [{"index":2}]}';
Map json = json.decode(jsonString);
Exercise.fromJson(json);
}
I found it out :)
I have restructured my fromJson to this, especially the jsonDecode was important, because json["exercise "] was only a String.
PlanModel.fromJson(dynamic json) {
name = json["name"];
if (json["exercise"] != null) {
exercise = [];
jsonDecode(json["exercise"]).forEach((v) {
exercise.add(Exercise.fromJson(v));
});
}}
now I can access it with
PlanModel item = snapshot.data[index];
item.exercise[0].timeGoal.toString()

Unable to load JSON data to show in ListView

I need to get the json array data in flutter app by providing the get method.
The problem that I am facing is that I am not able to put the records in the empty list carList.
but I am getting the array of objects as shown below(POSTMAN) by
print(json.decode(response.body)) // in the file cars.dart
Hence if you could please help me on how to get the response in the List carlist = [];
Car_list.dart file calls the method for the get request.
Please let me know if you require any further information from my end.
Thankyou
POSTMAN
[
{
"id": "1",
"carModel" : "Maruti swift",
"carDescription" : "The car has a top speed of 180 km/hr"
},
{
"id": "1",
"carModel" : "Hyundai santro",
"carDescription" : "The car has a top speed of 150 km/hr"
},
{
"id": "1",
"carModel" : "Hyundai creta",
"carDescription" : "The car has a top speed of 160 km/hr"
}
]
CarsModel.dart
class Cars with ChangeNotifier{
String userId;
String carModel
String carDescription;
Cars(
{
this.userId = '1111',
this.carModel,
this.carDescription,
}
);
factory Cars.fromJSON(Map<String,dynamic> json) => Cars(
userId : json['userId'],
carModel : json['CarModel'],
carDescription : json['carDescription'],
);
toJSON() {
return {
'userId':'111',
'carModel':carModel,
'carDescription' : carDescription
};
}
cars.dart
class CarProvider with ChangeNotifier{
List<Cars> carlist = [
//Sample data to show the format in which the data needs to be shown as the listview gets populated from below records which we are supposed to get from postman
//Cars (
// userid: 1111,
// carModel: 'Maruti Alto',
// carDescription: 'Top speed 140 km/hr'
),
];
Future<void> readListofCars(int id) async {
print(id.toString());
const url = 'http://localhost/userlist';
// {'id': id.toString()
Map<String, String> headers = {
"Content-type": "application/json"};
try
{
final response = await http.get(url,headers: headers);
List<dynamic> bodyCars = jsonDecode(response.body);
List<Cars> loadedCars = bodyCars.map((dynamic item) => new Cars.fromJSON(item)).toList();
/*
for (Map i in bodyCars) {
_notificationitems.add(Cars.fromJSON(i));
}
*/
});
print(response.statusCode);
carlist = loadedCars;
print(json.decode(response.body));
notifyListeners();
}catch (error){
throw error;
}
}
Car_list.dart
class TabScreenCar extends StatefulWidget {
final String userId;
TabScreenCar(this.userId);
#override
_TabScreenCarState createState() => _TabScreenCarState();
}
class _TabScreenCarState extends State<TabScreenCar> {
#override
void didChangeDependencies() {
final id = 1111;
Provider.of<CarProvider>(context).readListofCars(id);
super.didChangeDependencies();
}
In your model class you have incorrect variable to fetch the json data
to make the process simple you can have a look at this link
https://stackoverflow.com/a/58708634/9236994
just paste your json response in https://javiercbk.github.io/json_to_dart/
and use the json model class which will be generated.
your issue will be resolved.
variable which you are using doesn't match with your json response
OR to keep it simple
use below mentioned fromJSON method
factory Cars.fromJSON(Map<String,dynamic> json) => Cars(
userId : json['id'],
carModel : json['carModel'],
carDescription : json['carDescription'],
);
EDIT
I think you are facing issue with setting up the data in your list variable too.
use below shown code to fill the response into list if cars data
List<Cars> loadedCars = List<Cars>();
var data = jsonDecode(response.body);
data.forEach((val) {
loadedCars.add(Cars.fromJSON(val));
}