I have a rather large json file that I have downloaded using my flutter app from a web api that's around 200MB using
final response = Dio().get(uri, options: Options(responseType: ResponseType.plain));
(Using default Dio options also uses json decoding, resulting in an out of memory exception.
This is not directly part of my problem but maybe it helps)
Now the problem starts after obtaining the json string.
When I use jsonDecode on the response body I run out of memory. (maybe the same function Dio uses?)
final data = jsonDecode(response.body); this runs out of memory
The json object itself is an array with a lot of items with a format like this:
[
{"data": {independent data1}},
{"data": {independent data2}},
...
]
and i would be fine just decoding one item at a time instead of everything at once to reduce memory usage. Is there another way to do process all the items in this array? Something like
jsonArrayDecode(response.body, onItemDecode: (item) { /*do stuff with item */ });
Or do I have to write my own json reader that can sequentially decode it?
Thanks to #pskink, I managed to solve my problem. I used the ResponseType.stream from Dio to get stream which can be processed using the reviver function argument from the JsonDecoder. I process all the data in the reviver and ignore the data in the onData function of the listener.
...
final response = await dio.get(uri,
options: Options(responseType: ResponseType.stream);
Function reviver = (key, value) {
if (/*value is element from top level list*/) {
processItem(value);
return null;
}
return value;
}
final completer = Completer();
response.data.stream.cast<List<int>>()
.transform(utf8.decoder)
.transform(JsonDecoder(reviver))
.listen(null, onDone: () { completer.complete(); });
await completer.future;
...
If the problem is really that the data file is very large, then the problem can be solved by not storing unnecessary objects during parsing.
This will only make sense if not all of the data is needed, but only some of the data.
In this case, we are talking about filtering data on the fly, that is, directly during the data parsing process.
Below is a small example of how this can be implemented.
import 'package:fast_json/fast_json_selector.dart' as parser;
import 'package:fast_json/fast_json_selector.dart' show JsonSelectorEvent;
void main(List<String> args) {
// Select users from the list by indexes [2..3] and terminate selection
final users = <User>[];
final level = '{} data [] 0 {}'.split(' ').length;
final elementLevel = '{} data [] 0'.split(' ').length;
void select(JsonSelectorEvent event) {
final levels = event.levels;
if (levels.length == level) {
final index = event.levels[elementLevel - 1] as int;
if (index >= 2 && index <= 3) {
final map = event.lastValue as Map;
final user = User.fromJson(map);
users.add(user);
}
// Free up memory
event.lastValue = null;
if (users.length == 2) {
throw const _TerminateException();
}
}
}
try {
parser.parse(_data, select: select);
} on _TerminateException {
//
}
print(users.join(', '));
}
const _data = '''
{
"success":true,
"data":[
{
"id":1,
"name":"Leanne Graham",
"username":"Bret",
"email":"Sincere#april.biz",
"address":{
"street":"Kulas Light",
"suite":"Apt. 556",
"city":"Gwenborough",
"zipcode":"92998-3874",
"geo":{
"lat":"-37.3159",
"lng":"81.1496"
}
},
"phone":"1-770-736-8031 x56442",
"website":"hildegard.org",
"company":{
"name":"Romaguera-Crona",
"catchPhrase":"Multi-layered client-server neural-net",
"bs":"harness real-time e-markets"
}
},
{
"id":2,
"name":"Ervin Howell",
"username":"Antonette",
"email":"Shanna#melissa.tv",
"address":{
"street":"Victor Plains",
"suite":"Suite 879",
"city":"Wisokyburgh",
"zipcode":"90566-7771",
"geo":{
"lat":"-43.9509",
"lng":"-34.4618"
}
},
"phone":"010-692-6593 x09125",
"website":"anastasia.net",
"company":{
"name":"Deckow-Crist",
"catchPhrase":"Proactive didactic contingency",
"bs":"synergize scalable supply-chains"
}
},
{
"id":3,
"name":"Clementine Bauch",
"username":"Samantha",
"email":"Nathan#yesenia.net",
"address":{
"street":"Douglas Extension",
"suite":"Suite 847",
"city":"McKenziehaven",
"zipcode":"59590-4157",
"geo":{
"lat":"-68.6102",
"lng":"-47.0653"
}
},
"phone":"1-463-123-4447",
"website":"ramiro.info",
"company":{
"name":"Romaguera-Jacobson",
"catchPhrase":"Face to face bifurcated interface",
"bs":"e-enable strategic applications"
}
},
{
"id":4,
"name":"Patricia Lebsack",
"username":"Karianne",
"email":"Julianne.OConner#kory.org",
"address":{
"street":"Hoeger Mall",
"suite":"Apt. 692",
"city":"South Elvis",
"zipcode":"53919-4257",
"geo":{
"lat":"29.4572",
"lng":"-164.2990"
}
},
"phone":"493-170-9623 x156",
"website":"kale.biz",
"company":{
"name":"Robel-Corkery",
"catchPhrase":"Multi-tiered zero tolerance productivity",
"bs":"transition cutting-edge web services"
}
},
{
"id":5,
"name":"Chelsey Dietrich",
"username":"Kamren",
"email":"Lucio_Hettinger#annie.ca",
"address":{
"street":"Skiles Walks",
"suite":"Suite 351",
"city":"Roscoeview",
"zipcode":"33263",
"geo":{
"lat":"-31.8129",
"lng":"62.5342"
}
},
"phone":"(254)954-1289",
"website":"demarco.info",
"company":{
"name":"Keebler LLC",
"catchPhrase":"User-centric fault-tolerant solution",
"bs":"revolutionize end-to-end systems"
}
}
]
}''';
class Company {
final String name;
Company({required this.name});
#override
String toString() {
return name;
}
static Company fromJson(Map json) {
return Company(
name: json['name'] as String,
);
}
}
class User {
final String city;
final int id;
final String name;
User({required this.city, required this.id, required this.name});
#override
String toString() {
return '$id:$name from $city';
}
static User fromJson(Map json) {
return User(
city: json['address']['city'] as String,
id: json['id'] as int,
name: json['name'] as String,
);
}
}
class _TerminateException {
const _TerminateException();
}
Related
I have a localization map like:
'en': {
"timeIsUpTitle": "Time is Up!",
"workingTimeText": "You are working for{{}} {{}} {{}}.."
},
'tr': {
'timeIsUpTitle': 'Zaman doldu!',
"workingTimeText": "{}{}{}çalışıyorsun..."
},
how can I fill these {} sections on the outside? I have some getters for these strings such as,
String workingTimeText(String hours, String minutes, String seconds) => localeValues['workingTimeText']!;
String get timeIsUpTitle => localeValues['timeIsUpTitle']!;
Here is a simple example of how to update the Map. You can create your own function based on your preferences.
void main() {
for (var key in data.keys) {
Map<String, String>? value = data[key];
if (value == null) return;
value.update("workingTimeText", (value) => 'New Data');
}
data.values.forEach(print);
}
var data = {
"en": {
"timeIsUpTitle": "Time is Up!",
"workingTimeText": "You are working for{{}} {{}} {{}}.."
},
"tr": {
"timeIsUpTitle": "Zaman doldu!",
"workingTimeText": "{}{}{}çalışıyorsun..."
}
};
Output:
{timeIsUpTitle: Time is Up!, workingTimeText: New Data}
{timeIsUpTitle: Zaman doldu!, workingTimeText: New Data}
I solved with format package in pub dev (it's the same as python's format funct.)
Good afternoon, I may have found a small error, but I can't figure it out until now..
I have the following class in dart, the purpose of this class is to receive json and transform each field to object
class Attributes {
final String lpn; //itemId
final String type; //itemType (REEL,FEEDER,ETC)
final String feederContainer; //containerId
final String feederSide; //locationInContainer(A o B)
final String locationInTool; //locationInTool
Attributes(
{required this.lpn,
required this.type,
required this.feederContainer,
required this.feederSide,
required this.locationInTool});
factory Attributes.fromJson(Map json) {
return Attributes(
lpn: json['itemId'],
type: json['itemType'],
feederContainer: json['containerId'],
feederSide: json['locationInContainer'],
locationInTool: json['locationInTool'],
);
}
}
class PartNumberAt {
final String partNumber; //partNumber
final String quantity; //quantity
final String initialQuantity; //initialQuantity
PartNumberAt(
{required this.partNumber,
required this.quantity,
required this.initialQuantity});
factory PartNumberAt.fromJson(Map json) {
return PartNumberAt(
partNumber: json['partNumber'],
quantity: json['quantity'],
initialQuantity: json['initialQuantity'],
);
}
}
//partnumber RawMaterial
class ReelPartNumber {
final PartNumberAt partNumberAt;
ReelPartNumber({required this.partNumberAt});
factory ReelPartNumber.fromJson(Map json) {
return ReelPartNumber(
partNumberAt: PartNumberAt.fromJson(json['attributes']),
);
}
}
class ReelLpn {
Attributes? attributes;
ReelPartNumber? reelPartNumber;
ReelLpn(
{required Attributes attributes, required ReelPartNumber reelPartNumber});
factory ReelLpn.fromJson(Map json) {
return ReelLpn(
attributes: Attributes.fromJson(json['attributes']),
reelPartNumber: ReelPartNumber.fromJson(json['RawMaterial']),
);
}
}
and I have a file where I make http requests, the request returns the following
{
"attributes": {
"itemId": "0605007783",
"itemKey": "14992663",
"itemType": "REEL",
"itemTypeClass": "Component Lot",
"containerId": "FA0210AEF424292",
"locationInContainer": "B",
"toolContainerId": "SMT6",
"locationInTool": "10004-B",
"quarantineLocked": "false",
"expired": "false",
"initTmst": "2022-01-20T09:40:30.969-03:00"
},
"RawMaterial": {
"attributes": {
"partNumber": "11201312001166",
"partNumberDesc": "",
"supplierId": "DEFAULT",
"quantity": "2497.0",
"initialQuantity": "5000.0",
"rejectedQuantity": "3.0",
"manualAdjustmentQuantity": "548.0"
},
}
and the request is made as follows
Future<ReelLpn?> getReelData(String lpn) async {
http.Response response = await http.get(Uri.parse(apiUrl+'/$lpn'));
if (response.statusCode == 200) {
Map data = (json.decode(response.body)); //conver to json
print(data);
ReelLpn reelLpn = ReelLpn.fromJson(data); //transform json to ReelLpn
return reelLpn;
}
return null;
}
and I call the service as follows
ReelLpn? data = await CogiscanService().getReelData('0605007783');
print(data?.attributes?.type);
my problem starts there, when I print
print(data?.attributes?.type);
it returns null, I put several prints, in the ReelL class, Attributes and PartNumber, to see if I was reading the Map correctly, and they definitely read correctly.
So why when I want to access any of the fields does it return null?
Change your ReelLpn Constructor. you are not referencing the class members... thats why its always null.
ReelLpn({required this.attributes, required this.reelPartNumber});
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));
}
I'm getting a null return when I run the following code. Apparently I'm not accessing json correctly.
Is there another way to access json data? Or is it ok to use it like that?
Future Class - parsing json from a url
Future<List<User>> _getUsers() async {
var data = await http.get("https://...api.php");
if (data.statusCode == 200) {
print('Status Code 200: Ok!');
var jsonData = json.decode(data.body);
List<User> users = [];
for (var u in jsonData[0]) {
print(u[0]["title"]);
User user = User(u["id"], u["source"], u["desc"], u["link"], u["title"]);
users.add(user);
}
print(users.length);
return users;
} else {
throw Exception('Failed to load json');
}
}
Class
class User {
final int id;
final String source;
final String desc;
final String link;
final String title;
User(this.id, this.source, this.desc, this.link, this.title);
}
Basic json structure:
{
"0":{
"id":0,
"source":"XXX",
"link":"XXXX",
"title":"XXXX",
"desc":"XXXX"
},
"1":{
"id":1,
"source":"XXX",
"link":"XXXX",
"title":"XXXX",
"desc":"XXXX"
}
}
What am missing here? thanks.
You're trying to use something that isn't a list as a list. The json structure you've provided looks like part of an object and not a list.
A list would look like this:
[{
"id": 0,
"source": "XXX",
"link": "XXXX",
"title": "XXXX",
"desc": "XXXX"
}]
But since the underlying structure is a Map you could iterate over the keys, doing something like this:
for (var k in jsonData.keys) {
var u = jsonData[k];
print(u["title"]);
User user = User(u["id"], u["source"], u["desc"], u["link"], u["title"]);
}
Your json structure is not a real json. But if your json is like this:
{
"0":{
"id":0,
"source":"XXX",
"link":"XXXX",
"title":"XXXX",
"desc":"XXXX"
},
"1":{
"id":1,
"source":"XXX",
"link":"XXXX",
"title":"XXXX",
"desc":"XXXX"
}
}
You can get data like this:
for (int i = 0; i < length; i ++) {
User user = User(u["$i"]["id"], u["$i"]["source"], u["$i"]["desc"], u["$i"]["link"], u["$i"]["title"]);
users.add(user);
}
I just started learning about flutter and try to use ScopedModel
and I get stuck at parsing JSON,
everything works until I try to get List of detail simulation but everything that I try just fails
this is my class for project and simulation
class Project {
String idProject;
List images;
List<Simulations> simulation;
Project({
#required this.idProject,
#required this.images,
#required this.simulation
});
}
class Simulations{
String period;
int profitProject;
int roi;
Simulations({
#required this.period,
#required this.profitProject,
#required this.roi
});
}
I made a separate class to get detail for simulation
this is a sample from JSON that I get from the firebase
{
"1554182068913": {
"idProject": "project id 1",
"images": [
1554181958565,
1554181955542,
1554181960876],
"simulation": [
{
"periode": "year 1",
"profitProject": 300000,
"roi": 5,
"uniqueKey": "YyCWbHjvm"
},
{
"periode": "year 1",
"profitProject": 100000,
"roi": 3,
"uniqueKey": "CvyU4SjrX"
},
{
"periode": "year 1",
"profitProject": 2000000,
"roi": 10,
"uniqueKey": "Tb_Qr5CIA"
}
],
}
}
I made function to get the data
Future<Null> fetchProjects() {
return http.get("JSON link")
.then<Null>((http.Response response) {
final List<Project> fetchedProjectList = [];
final Map<String, dynamic> projectListData = json.decode(response.body);
if (projectListData == null) {
_isLoading = false;
notifyListeners();
return;
}
projectListData.forEach((String projectId, dynamic projectData) {
final Project project = Project(
title: projectData['title'],
location: projectData['location'],
images: projectData['images'],
simulation: ???
);
fetchedProjectList.add(project);
}
What's missing here is JSON serialization. You need to convert the response to a Dart object, similar to what has been demonstrated on this guide. In Project class, you can add a fromJson() constructor to map the data.
factory Project.fromJson(Map<String, dynamic> json) {
return Project(
idProject: json['idProject'],
images: json['images'],
simulation: json['simulation'],
);
}