Flutter How to convert List into and from JSON - flutter

There are two Functions that I use to convert two String variables into JSON and from JSON.
String toJson() {
Map<String,dynamic> map = {'name': name,'count':checkListCount,'description':description,};
return jsonEncode(map)
}
fromJson(String context){
Map<String,dynamic> map = jsonDecode(contents)
name = map['name'];
description = map['description'];
return '0';
}
How i can use this to functions to covert List?
There is my list
List<CheckListPoint> checkListPoints = [];
CheckListPoint{
bool correctly = false;
bool passed = false;
String requirement = '';
}
variables that I have in CheckListPoint will change by the user later in the app.

i am not exactly getting which key will give you the list of data from your question,
but suppose you have a response in with "data" key is giving you list of items,
then you can add from JSON with ->
data : List<User>.from(json["data"].map((x) => User.fromJson(x))),
and to convert it to JSON you need
"data": List<dynamic>.from(data.map((x) => x.toJson())),
i hope this is what you are asking for

I did a very similiar thing when fetching messages from firebase. It's the same as parsing from JSON since both values are dynamic.
Here I convert a DataSnapshot to a List
Future<List<Message>> getMessages(DataSnapshot snapshot) async {
List<Message> msgs = List.empty(growable: true);
if(snapshot.value != null) {
Map<dynamic, dynamic> messages = snapshot.value;
messages.forEach((key, value) {
msgs.add(Message.fromFirebase(value));
});
}
return msgs;
}
And here is the Message class:
class Message {
String message;
String senderId;
int time;
Message.fromFirebase(Map<dynamic, dynamic> json) :
message = json["message"],
senderId = json["senderId"],
time = json["time"];
}

Related

A field in JSON can come in with different names, can I represent it with a single struct in Flutter?

I have a JSON that can come up with a different name each time. How can I make a single factoy that receives the information regardless of the title?
factory Filmes.fromJson(Map<String, dynamic> json) {
final title = json['original_title'];
}
depending on the API link, the title can come as 'original_title' or 'original_name'.
how can I make that '' title '' receive or one or the other? there will never be a json that comes with both options.
Thanks
Future<List<Filmes>> getFilmes({String movieOrTv}) async {
try {
Response response = await Dio().get(
'https://api.themoviedb.org/3/$movieOrTv/popular?api_key=&page=1',
);
final json = response.data['results'];
List<Filmes> filmes = json
.map<Filmes>((filmesJson) => Filmes.fromJson(filmesJson))
.toList();
criarFilmes(filmes: filmes);
} catch (e) {
print(e);
return [];
}
}
you can access the keys of your retrieved JSON and then handle the assignment using it.
and you can use null operator to check values
factory Filmes.fromJson(Map<String, dynamic> json) {
print(json.keys); // here is your keys
final title = json['original_title'] ?? json['original_name'];
}

Flutter error when converting a list to a json object removing some keys

I have an error when trying to convert a list of my object to json
My error:
Unhandled Exception: type 'RxList<ItemStockEntryModel>' is not a subtype of type 'Map<dynamic, dynamic>'
My model code:
class StockEntryModel {
final int? id;
final double costFreight;
final List<ItemStockEntryModel> items;
StockEntryModel({
this.id,
required this.costFreight,
required this.items,
});
factory StockEntryModel.fromJson(Map<String, dynamic> json) =>
StockEntryModel(
id: json['id'],
costFreight: json['costFreight'],
items: json['itemStockEntries'],
);
Map<String, dynamic> toJson() => {
'id': id,
'costFreight': costFreight,
'itemStockEntries': items,
};
Map<String, dynamic> itemsToMap() => {
'data': items,
};
String itemsToJson() {
var data = {};
final test = itemsToMap()['data'];
final mappedItems = Map<String, dynamic>.from(test) // the error occurs here on test variable
..removeWhere((key, value) => value == null || key == 'product');
print(json.encode(mappedItems));
data['itemStockEntries'] = mappedItems;
return json.encode(data);
}
}
my goal is to return a json object like this
// is not complete, only example...
{
"itemStockEntries": {
"data": [{
"id": 2
}, {
"id": 3
}]
}
}
but i need remove keys if this value is null and my key product..
I saw some similar errors, but I couldn't find the one that actually causes it
sorry for my bad english =(
My solution based on Loren codes. I expect to help someone also
Map<String, dynamic> toJson() => {
'id': id,
'costFreight': costFreight,
'itemStockEntries': items.map((e) => e.toJson()).toList(),
};
Map<String, dynamic> itemsToMap() => {
'data': items
.map(
(e) => e.toJson()
..removeWhere(
(key, value) => key == 'product' || value == null),
)
.toList(),
};
Map<String, dynamic> modelToJson() {
Map<String, dynamic> data = {};
data['itemStockEntries'] = itemsToMap();
data['costFreight'] = costFreight;
print(json.encode(data));
return data;
}
The .from method on a map needs a map to be passed into it, and you're passing in a list. So removeWhere is looking for keys and values which don't exist the way you're doing it.
So you could clear that first error getting rid of the itemsToMap function and changing the first 2 lines of your itemsToJson function to this.
var data = {'data': items}; // an actual map that you can pass in
final mappedItems = Map<String, dynamic>.from(data) // no more error here
But that's still a map with just a single key with a value of a list. So the removeWhere is not going to do anything of value here.
The List<ItemStockEntryModel> is what you need to be iterating through.
Assuming you have json serialization setup in your ItemStockEntryModel, this is closer to what you need to do. Not a complete example because I don't know what that model looks like, but it should give you the idea.
String itemsToJson() {
Map data = {};
List<String> jsonList = []; // new list of json strings to pass into data map
for (final item in items) {
if (// item meets whatever conditions you need) {
final jsonItem = json.encode(item);
jsonList.add(jsonItem);
}
}
data['itemStockEntries'] = {'data': jsonList};
return json.encode(data);
}

Flutter: How to load JSON into a PageView

I'm hoping someone can tell me how to load JSON in to a PageView. Each page in the PageView will contain a ListView which will use Card widgets to display each Job from the JSON.
The JOB_DATE will dictate which page the job is displayed on. So in the JSON below, the first 3 items are on one date and the next 2 items are on the following date. So page 1 should display the first 3 items and page 2 should display items 4 & 5.
JSON :
{
"rows":[
{ "JOBID":23, "JOB_DATE":1588809600000, "START_TIME":"07:30", "JOB_NAME":"Cleaner" },
{ "JOBID":24, "JOB_DATE":1588809600000, "START_TIME":"08:30", "JOB_NAME":"Manager" }
{ "JOBID":25, "JOB_DATE":1588809600000, "START_TIME":"12:30", "JOB_NAME":"Caretaker" }
{ "JOBID":26, "JOB_DATE":1588896000000, "START_TIME":"08:30", "JOB_NAME":"Manager" }
{ "JOBID":27, "JOB_DATE":1588896000000, "START_TIME":"13:30", "JOB_NAME":"Caretaker" }
]
}
How would I code this to split the JSON up to the different pages?
Thanks heaps for any help.
Cheers,
Paul
You can you groupBy function from 'package:collection/collection.dart'
var json = {
"rows":[
{ "JOBID":23, "JOB_DATE":1588809600000, "START_TIME":"07:30", "JOB_NAME":"Cleaner" },
{ "JOBID":24, "JOB_DATE":1588809600000, "START_TIME":"08:30", "JOB_NAME":"Manager" }
{ "JOBID":25, "JOB_DATE":1588809600000, "START_TIME":"12:30", "JOB_NAME":"Caretaker" }
{ "JOBID":26, "JOB_DATE":1588896000000, "START_TIME":"08:30", "JOB_NAME":"Manager" }
{ "JOBID":27, "JOB_DATE":1588896000000, "START_TIME":"13:30", "JOB_NAME":"Caretaker" }
]
}
List<Map<String, dynamic> rows = json['rows']
Map<dynamic, List<Map<String, dynamic>> sortedRow = groupBy(rows, (row) => row['JOB_DATE']
And you will have a map where keys are (1588809600000, 1588896000000) and values are lists of your objects.
After that you can create PageView with a ListView of your objects
I would use something like this Json to dart class converter to quickly get a dart class for you JSON data structure. Then I would use the resulting dart class to parse your Json into a list of jobs in dart and then use that list on a specific page's ListView data source by only selecting the values with the specific sTARTTIME value you want to display on that page.
class Job {
List<Rows> rows;
Job({this.rows});
Job.fromJson(Map<String, dynamic> json) {
if (json['rows'] != null) {
rows = new List<Rows>();
json['rows'].forEach((v) {
rows.add(new Rows.fromJson(v));
});
}
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
if (this.rows != null) {
data['rows'] = this.rows.map((v) => v.toJson()).toList();
}
return data;
}
}
class Rows {
int jOBID;
int jOBDATE;
String sTARTTIME;
String jOBNAME;
Rows({this.jOBID, this.jOBDATE, this.sTARTTIME, this.jOBNAME});
Rows.fromJson(Map<String, dynamic> json) {
jOBID = json['JOBID'];
jOBDATE = json['JOB_DATE'];
sTARTTIME = json['START_TIME'];
jOBNAME = json['JOB_NAME'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['JOBID'] = this.jOBID;
data['JOB_DATE'] = this.jOBDATE;
data['START_TIME'] = this.sTARTTIME;
data['JOB_NAME'] = this.jOBNAME;
return data;
}
}
You can parse our JSON like this in your flutter app:
import 'dart:convert';
...
Job jobs = Job.fromJson(json.decode(jsonString));
...
final firstPageData = jobs.rows.where((row) => row.jOBDATE == 1588809600000).toList();
final secondPageData = jobs.rows.where((row) => row.jOBDATE == 1588896000000).toList();

Flutter Firestore array with Maps. Do they work?

I can't stream (read) Documents that contain array properties that are Maps in Firestore.
Using Firestore with a document containing an array with a simple String type works as expected. Easy to write (append with FieldValue.arrayUnion(['data1','data2','data3']) and stream back out with code like:
var test2 = List<String>();
for (var item in data['items']) {
print('The item $item');
test2.add(item);
}
test2 can now be used as my items property.
When I try and use a List where item type becomes a Map in Firestore and is just a simple Class containing a few Strings and a date property. I can write these to FireStore but I can't read them back out.
The following code fails: (no errors in the Debug Console but it doesn't run)
var test2 = List<Item>();
data['items'].forEach((item) {
var description = item['description'];
print('The description $description'); // <-- I don't get past this
final x = Item.fromMap(item); // <-- so I can't even attempt this
return test2.add(x);
});
I never get to actually call my Item.fromMap constructor: here is another try:
// fails
final theItems = data['items'].map((x) {
return Item.fromMap(x); // <-- never get here
});
Although the DEBUG CONSOLE doesn't say there is any problem. If I inspect theItems variable (the debugger 'jumps' a couple of lines down to my return) after the failed iteration it looks like this:
MappedListIterable
_f:Closure
_source:List (1 item)
first:Unhandled exception:\ntype '_InternalLinkedHashMap<dynamic, dynamic>' is not a subtype of type 'Map<String, dynamic>'\n#0 new Listing.fromMap.<anonymous closure> isEmpty:false
isNotEmpty:true
iterator:ListIterator
last:Unhandled exception:\ntype '_InternalLinkedHashMap<dynamic, dynamic>' is not a subtype of
So bad things have happened but I have no idea why!
Has anyone actually written and retrieved Firestore array properties that contain Maps?
Any help in how to proceed would be greatly appreciated!
More: screen shot of the document
Here is the code that reads (streams) the collection
Stream<List<T>> collectionStream<T>({
#required String path,
#required T builder(Map<String, dynamic> data, String documentID),
#required String userId,
Query queryBuilder(Query query),
int sort(T lhs, T rhs),
}) {
Query query = Firestore.instance.collection(path);
if (queryBuilder != null) {
query = queryBuilder(query);
}
final Stream<QuerySnapshot> snapshots = query.snapshots();
return snapshots.map((snapshot) {
//print('document: path $path ${snapshot.documents[0]?.documentID}');
final result = snapshot.documents
.map((snapshot) => builder(snapshot.data, snapshot.documentID))
.where((value) => value != null)
.toList();
if (sort != null) {
result.sort(sort);
}
print('returning from CollectionStream');
return result;
});
}
the .map is where the problem comes in. The builder function resolves to this:
builder: (data, documentId) {
return Listing.fromMap(data, documentId);
},
Which ends up here
factory Listing.fromMap(
Map<String, dynamic> data,
String documentId,
) {
if (data == null) {
return null;
}
final theTime = (data['createdAt'] as Timestamp).toDate();
// see above code where I fail at getting at the items property/field
Here is the Item Class:
class Item {
Item(this.description, this.imageURL, this.thumbnailURL,
{this.status = ItemStatus.active,
this.type = ListingType.free,
this.price = 0,
this.isUpdate = false,
this.createdAt});
final String description;
final String imageURL;
final String thumbnailURL;
final ItemStatus status;
final ListingType type;
final int price;
final bool isUpdate;
DateTime createdAt;
factory Item.fromMap(
Map<String, dynamic> data,
) {
if (data == null) {
return null;
}
final theTime = (data['createdAt'] as Timestamp).toDate();
return Item(
data['description'],
data['imageURL'],
data['thumbnailURL'],
status: _itemStatusFromString(data['status']),
type: data['type'] == 'free' ? ListingType.free : ListingType.flatRate,
createdAt: theTime,
);
}
Map<String, dynamic> toMap() {
// enums are for shit in Dart
final statusString = status.toString().split('.')[1];
final typeString = type.toString().split('.')[1];
return {
'description': description,
'imageURL': imageURL,
'thumbnailURL': thumbnailURL,
'itemStatus': statusString,
'price': price,
'listingType': typeString,
if (!isUpdate) 'createdAt': DateTime.now(),
if (isUpdate) 'updatedAt': DateTime.now(),
};
}
}
The above is never called to read (we crash) ... it is called to write the data.
This is a known issue with Firestore. Here is the starting thread Flutter issues I found
From reading through the issues it seems lots of folks use a serializer package which is where the issue surfaced. Its still active...
Here is my solution NOT using a serializer and just doing it 'by hand'.
I was able simplify the problem and generate an error that was Google-able. Below is a single page which just writes and reads a single Document. The Class A and B are as small as can be.
So the code writes a single document to Firestore the contains two properties. A name and items. The items being the List of Class B that Class A contains. Checkout the Firebase console. The reading just does that.
No StreamController just the Console. The problem is in the fromMap method where we have to convert the array of objects in Firesote to a List of class instances in Dart. This should not be this difficult and at a minimum it should be documented ....
the line
var theRealItems = data['items'].map((i) => B.fromMap(i));
will generate the error. And needs to be replaced with
var theItems = data['items'].map((i) {
var z = Map<String, dynamic>.from(i);
print(z['description']);
return B.fromMap(z);
}).toList();
var theRealItems = List<B>.from(theItems);
Why this is so difficult is still a mystery to me! Anyone improving this code: I'm all ears.
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:flutter/material.dart';
class A {
A(this.name, this.items);
final name;
final List<B> items;
factory A.fromMap(
Map<String, dynamic> data,
String documentId,
) {
if (data == null) {
return null;
}
// we crash here !!!
var theRealItems = data['items'].map((i) => B.fromMap(i));
// uncomment the 6 lines below
// var theItems = data['items'].map((i) {
// var z = Map<String, dynamic>.from(i);
// print(z['description']);
// return B.fromMap(z);
// }).toList();
// var theRealItems = List<B>.from(theItems);
return A(data['name'], theRealItems);
}
Map<String, dynamic> toMap() {
var theItems = items.map((i) => i.toMap()).toList();
return {'name': name, 'items': theItems};
}
}
class B {
B(this.description);
final description;
factory B.fromMap(
Map<String, dynamic> data,
) {
if (data == null) {
return null;
}
return B(data['description']);
}
Map<String, dynamic> toMap() {
return {
'description': description,
};
}
}
class Test extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Container(
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
RaisedButton(
onPressed: () => _write(),
child: Text('Write the Doc'),
),
RaisedButton(
onPressed: () => _read(),
child: Text('Read the Doc ... check Debug Console!'),
),
],
),
),
);
}
_write() async {
try {
var b = B('Inside B!');
List<B> theList = List<B>();
theList.add(b);
var a = A('myName', theList);
await Firestore.instance
.collection('test')
.document('testDoc')
.setData(a.toMap());
print('returning from write!');
} catch (e) {
print('Error ${e.toString()}');
}
}
_read() async {
try {
var aFromFs = await Firestore.instance
.collection('test')
.document('testDoc')
.get();
var a = A.fromMap(aFromFs.data, aFromFs.documentID);
print('the A from FireBase $a with name ${a.name} first item ${a.items.first.description}');
print('returning from read!');
} catch (e) {
print('Oh no Error! ${e.toString()}');
}
}
}
In the below code Name refers to the name of your respective document and collection.
Let's say you want to get "imageURL" and "thumbnailUrl" for now and update the values without deleting or changing other fields inside the array.
String imageUrl ;
String thumbnailUrl;
DocumentReference _docReference = FirebaseFirestore.instance.collection(NAME).doc(NAME);
//refering to the specific document
Map<String, dynamic> neededData= allDocsFromTraining.data();
//getting all the keys and values inside the document
List<Map<String, dynamic>> _yourDocument=
(neededData["items"] as List<dynamic>)
.map((m) => Map<String, dynamic>.from(m))
.toList();
//only getting the list of items
for (int index = 0; index < _yourDocument.length; index++) {
Map<String, dynamic> _getMap = _yourDocument[index];
if (_getMap["price"] == 0)
//giving a condition to get the desired value and make changes
{
//setting the variables with the value from the database
setState(() {
imageUrl = _getMap["imageURL"];
thumbnailUrl = _getMap["thumbnailURL"]
});
///Use this if you want to update value of imageURL and thumbnailURL at that specific index of an array
_getMap['imageURL'] = "Your Updated URL";
_getMap['thumbnailURL'] = "Your Updated Url;
break;
} else {}
}
await _docReference.update({"items": _yourDocument});
//this adds the updated value to your database.

Convert list of object with array to and from json file

I want to convert a list of 'Box' objects to a json file and also read it back in (json file to a list a 'Box' objects), but I'm a bit stuck on the implementation. I have written the below code, but I can only write a single 'Box' object to the json and convert a single 'Box' object back. When I try to do this with a list I hit errors like data that gets overwritten or just a single object that gets returned.
So in short, I want to write a List to json and convert json to List
I have the following data structure
Box model
class Box {
final String nameBox;
final List<Item> items;
Box({#required this.nameBox, #required this.items});
factory Box.fromJson(Map<String, dynamic> json) {
var items = json['items'];
List<Item> itemsList = items.cast<Item>();
return new Box(
nameBox: json["nameBox"],
items: itemsList
);
}
Map<String, dynamic> toJson() => {
"nameBox": nameBox,
"items": items,
};
}
Box fromJson(String boxData) {
return Box.fromJson(json.decode(boxData));
}
String toJson(Box box) {
return json.encode(box.toJson());
}
Item model
class Item {
final String itemName;
final int quantity;
Item({#required this.itemName, #required this.quantity});
factory Item.fromJson(Map<String, dynamic> json) {
return new Item(itemName: json["itemName"], quantity: json["quantity"]);
}
Map<String, dynamic> toJson() => {
"itemName": itemName,
"quantity": quantity,
};
}
Item fromJson(String itemData) {
return Item.fromJson(json.decode(itemData));
}
String toJson(Item item) {
return json.encode(item.toJson());
}
writeToJson function
Future writeJson(Box box) async {
final file = await _localFile;
List<Box> tempRead = await returnBoxes();
if (tempRead.isEmpty || tempRead == null) {
return;
}
tempRead.add(box);
file.writeAsString(json.encode(tempRead));
}
readJson function
Future<List<Box>> returnBoxes() async {
final file = await _localFile;
List<Box> boxList = new List<Box>();
Map<String, dynamic> content = await json.decode(file.readAsStringSync());
boxList.add(Box.fromJson(content));
return boxList;
}
I also tried to cast the json content to a list, but then I hit some iterable errors. Any who can help me out?
JSON has this idiosyncrasy that everything is either an object or an array, and you don't know what you get until you decode it. Dart decodes the two json types into a Map<String, dynamic> and List<dynamic> respectively. (The reason that you get dynamic is because each of them could itself then be a value, a json array or a json object, recursively.)
Dart encodes a Dart object by calling toJson on it and a Dart list by emitting a [ then each member of the list then a ].
Knowing that, it's easy to encode and decode arrays/lists. (I removed all the unnecessary code.)
class Box {
final String nameBox;
final List<Item> items;
Box({#required this.nameBox, #required this.items});
factory Box.fromJson(Map<String, dynamic> json) => Box(
nameBox: json['nameBox'],
items: json['items'].map<Item>((e) => Item.fromJson(e)).toList(),
);
Map<String, dynamic> toJson() => {
'nameBox': nameBox,
'items': items,
};
}
class Item {
final String itemName;
final int quantity;
Item({#required this.itemName, #required this.quantity});
factory Item.fromJson(Map<String, dynamic> json) => Item(
itemName: json['itemName'],
quantity: json['quantity'],
);
Map<String, dynamic> toJson() => {
'itemName': itemName,
'quantity': quantity,
};
}
Future writeJson(Box box) async {
final file = await _localFile;
var boxes = await returnBoxes();
/* I think you probably don't want this...
if (boxes.isEmpty || boxes == null) {
return;
}*/
// but rather, this
if (boxes == null) boxes = [];
boxes.add(box);
await file.writeAsString(json.encode(boxes));
}
Future<List<Box>> returnBoxes() async {
final file = await _localFile;
// because the json is an array (i.e. enclosed in []) it will be decoded
// as a Dart List<dynamic>
List<dynamic> d = json.decode(await file.readAsString());
// here we map the List<dynamic> to a series of Box elements
// each dynamic is passed to the Box.fromJson constructor
// and the series is formed into a List by toList
return d.map<Box>((e) => Box.fromJson(e)).toList();
}