Problems parsing complex json in Flutter - arrays

I am having issues converting the following response ->
{
"data": [
{
"_id": "1AoJoWJ5Hdx3nZ5t",
"title": "Orange is the new black",
"imageUrl": "/1532353304050-oinb.jpg",
"likesCount": 674
},
{
"_id": "AeqiQJewtTvMPc1B",
"title": "X-Files",
"imageUrl": "/1532353346638-xfiles.png",
"likesCount": 6155
},
{
"_id": "gPkzfXoJXX5TuTuM",
"title": "Star Trek: Voyager",
"imageUrl": "/1532353336145-voyager.jpg",
"likesCount": 23
},
{
"_id": "vQBQcYwtF9GWWJyK",
"title": "South Park",
"imageUrl": "/1532353313669-southpark.jpg",
"likesCount": 56
},
{
"_id": "wjLUixBQ4sirMAYw",
"title": "The Simpsons",
"imageUrl": "/1532353326075-thesimpsons.jpg",
"likesCount": 65
}
]
}
I tried using the jsonserializer plugin as well as the json_annotations plugin but got nowhere.
I did try to get a parser class with quicktype.io but It seems to not work at all.
Can someone please guide me or assist me with this issue?
Thanks!

There is a good plugin for that in Android Studio.
JSON to Dart Class.
Once you install the plugin do as follow.
Press ALT + L
Give a Class Name and paste your JSON response
And you are done.
After you get a response do as follow as
import 'dart:convert';
var decodedData = json.decode(response.body);
var data = Data.fromJson(decodedData)
If you need the status code of you response then response.statusCode

I followed this official document of Flutter and it works for me.
https://flutter.dev/docs/development/data-and-backend/json
Follow these steps to solve your problem.
Add dependencies as shown in the document.
dependencies:
# Your other regular dependencies here
json_annotation: <latest_version>
dev_dependencies:
# Your other dev_dependencies here
build_runner: <latest_version>
json_serializable: <latest_version>
Create stackoverflow.dart
import 'package:json_annotation/json_annotation.dart';
part 'stackoverflow.g.dart';
#JsonSerializable()
class StackOverFlowModel {
#JsonKey(name: '_id')
String id;
String title;
String imageUrl;
int likesCount;
StackOverFlowModel();
factory StackOverFlowModel.fromJson(Map<String, dynamic> json) =>
_$StackOverFlowModelFromJson(json);
Map<String, dynamic> toJson() => _$StackOverFlowModelToJson(this);
}
Giving variable name as _id will confuse Dart with a private variable. It is better to give it a JSON name using JsonKey annotation.
Run flutter pub run build_runner build in the terminal.
stackoverflow.g.dart will be generated like this.
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'stackoverflow.dart';
// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************
StackOverFlowModel _$StackOverFlowModelFromJson(Map<String, dynamic> json) {
return StackOverFlowModel()
..id = json['_id'] as String
..title = json['title'] as String
..imageUrl = json['imageUrl'] as String
..likesCount = json['likesCount'] as int;
}
Map<String, dynamic> _$StackOverFlowModelToJson(StackOverFlowModel instance) =>
<String, dynamic>{
'_id': instance.id,
'title': instance.title,
'imageUrl': instance.imageUrl,
'likesCount': instance.likesCount
};
For testing do this
Map map = {
"data": [
{
"_id": "1AoJoWJ5Hdx3nZ5t",
"title": "Orange is the new black",
"imageUrl": "/1532353304050-oinb.jpg",
"likesCount": 674
},
{
"_id": "AeqiQJewtTvMPc1B",
"title": "X-Files",
"imageUrl": "/1532353346638-xfiles.png",
"likesCount": 6155
},
{
"_id": "gPkzfXoJXX5TuTuM",
"title": "Star Trek: Voyager",
"imageUrl": "/1532353336145-voyager.jpg",
"likesCount": 23
},
{
"_id": "vQBQcYwtF9GWWJyK",
"title": "South Park",
"imageUrl": "/1532353313669-southpark.jpg",
"likesCount": 56
},
{
"_id": "wjLUixBQ4sirMAYw",
"title": "The Simpsons",
"imageUrl": "/1532353326075-thesimpsons.jpg",
"likesCount": 65
}
]
};
List<StackOverFlowModel> list = List.generate(map['data'].length,
(index) => StackOverFlowModel.fromJson(map['data'][index]));
print(list);

Related

How to update array inside MongoDB document

Can someone help me with a solution to update an array object inside the MongoDB document, I've tried a couple of methods but still it's to updating, here is my document that I want to update the array in the document.
{
"title": "Products",
"description": "test",
"image": "bdd8510d75f6e83ad308d5f306afccef_image.jpg",
"_created_at": "2021-06-07T20:51:08.316Z",
"ratingCount": 0,
"ratingTotal": 0,
"placeListSave": [
{
"objectId": "g70brr45pfi",
"name": "Kale",
"email": "null",
"strBrandLogo": "84de8865e3223d1ca61386355895aa04_image.jpg",
"storeNumber": "56",
"phone": "0815342119",
"createdAt": "2021-06-10T10:19:53.384Z",
"image": "ad1fb7602c2188223fd891a52373cb9d_image.jpg"
},
{
"objectId": "0qokn33p773",
"name": "Apple",
"email": null,
"strBrandLogo": null,
"storeNumber": "01",
"phone": "011 393 8600",
"createdAt": "2021-06-11T03:11:17.342Z",
"image": "8cfcbf2bcb5e3b4ea8ade44d3825bb52_image.jpg"
}
]
}
So I only want to update the apple object and change the data, I've tried the following code but doesn't seem to work.
`
var db = client.db("test");
try {
db.collection("ShoppingCentres").updateOne({
"title": req.body.product,
"placeListSave.objectId": req.body.id,
}, {
$set: {
"placeListSave.$.email": req.body.email,
"placeListSave.$.storeNumber": req.body.storeNumber,
"placeListSave.$.phone": req.body.phone,
"placeListSave.name": req.body.name,
},
});
res.json("client");
} catch (e) {
console.log("verify", e);
}
});`
arrayFilters seems suitable here:
db.collection.update({
"title": "Products",
"placeListSave.objectId": "0qokn33p773",
},
{
$set: {
"placeListSave.$[x].email": "test#some.email",
"placeListSave.$[x].storeNumber": "test",
"placeListSave.$[x].phone": "test",
"placeListSave.$[x].name": "test"
}
},
{
arrayFilters: [
{
"x.objectId": "0qokn33p773"
}
]
})
explained:
Add array filter called "x" with the objectId for the element that you need to update and use this filter in the $set stage to update the necessary elements.
Hint: To speed up the update you will need to add index on title field or compound index on title+placeListSave.objectId
playground

Unable to use forEach in Flutter Dart for array with just Strings

I'm trying to fill an array with the info of another array that is obtained from an API call.
I'm using the for each function of the Array with the data but I'm getting this error:
E/flutter (21633): [ERROR:flutter/lib/ui/ui_dart_state.cc(177)] Unhandled Exception: type 'String' is not a subtype of type 'int' of 'index'
I understand it is because I'm using the imdbID as the index and that is a String however in my response from the API all the items on the Array come as String.
Example:
{
"Title": "The Avengers",
"Year": "2012",
"Rated": "PG-13",
"Released": "04 May 2012",
"Runtime": "143 min",
"Genre": "Action, Adventure, Sci-Fi",
"Director": "Joss Whedon",
"Writer": "Joss Whedon (screenplay), Zak Penn (story), Joss Whedon (story)",
"Actors": "Robert Downey Jr., Chris Evans, Mark Ruffalo, Chris Hemsworth",
"Plot": "Earth's mightiest heroes must come together and learn to fight as a team if they are going to stop the mischievous Loki and his alien army from enslaving humanity.",
"Language": "English, Russian, Hindi",
"Country": "USA",
"Awards": "Nominated for 1 Oscar. Another 38 wins & 79 nominations.",
"Poster": "https://m.media-amazon.com/images/M/MV5BNDYxNjQyMjAtNTdiOS00NGYwLWFmNTAtNThmYjU5ZGI2YTI1XkEyXkFqcGdeQXVyMTMxODk2OTU#._V1_SX300.jpg",
"Ratings": [
{
"Source": "Internet Movie Database",
"Value": "8.0/10"
},
{
"Source": "Rotten Tomatoes",
"Value": "91%"
},
{
"Source": "Metacritic",
"Value": "69/100"
}
],
"Metascore": "69",
"imdbRating": "8.0",
"imdbVotes": "1,263,208",
"imdbID": "tt0848228",
"Type": "movie",
"DVD": "N/A",
"BoxOffice": "$623,357,910",
"Production": "Marvel Studios",
"Website": "N/A",
"Response": "True"
}
Here is my Current code for my call:
Future<void> fetchAndSetPeliculas(title, tipo) async {
var url = 'http://www.omdbapi.com/?apikey=[my API key]&t=$title';
try {
final response = await http.get(url);
final extractedData = json.decode(response.body) as Map<String, dynamic>;
if(extractedData == null){
return;
}
final List<Pelicula> loadedPeliculas = [];
extractedData.forEach((imdbID, data) {
loadedPeliculas.add(Pelicula(
imdbID: imdbID,
imdbRating: data['imdbRating'],
metaScore: data['Metascore'],
plot: data['Plot'],
poster: data['Poster'],
title: data['Title'],
type: data['Type'],
year: data['Year'],
));
});
_items = loadedPeliculas.reversed.toList();
notifyListeners();
} catch (error) {
throw (error);
}
}
So is there a way to fill the loadedPeliculas Array with the information from the API call, or move in the Response Array when there is no INT value for it?
Kind Regards.
Any specific reason for casting your response to Map<String, dynamic>. A List should work just fine.
This should work as well:
try {
final response = await http.get(url);
final extractedData = json.decode(response.body) as List;
if(extractedData == null){
return;
}
final List<Pelicula> loadedPeliculas = [];
extractedData.forEach((data) {
loadedPeliculas.add(Pelicula(
imdbID: data['imdbID'],
imdbRating: data['imdbRating'],
metaScore: data['Metascore'],
plot: data['Plot'],
poster: data['Poster'],
title: data['Title'],
type: data['Type'],
year: data['Year'],
));
});
Edit: You might not even have to add as List
Thanks Rey,
Your Answer got me to the final Answer the Problem as you Abion47 mentioned was that I was mapping the response.
Although it gave me the error on my previous Comment I was now able to move inside the JSON array using this code:
try {
final response = await http.get(url);
final extractedData = json.decode(response.body);
if (extractedData == null) {
return;
}
final List<Pelicula> loadedPeliculas = [];
final size = extractedData.length;
for (var i = 0; i < size; i++) {
loadedPeliculas.add(Pelicula(
imdbID: extractedData['Search'][i]['imdbID'],
poster: extractedData['Search'][i]['Poster'],
title: extractedData['Search'][i]['Title'],
type: extractedData['Search'][i]['Type'],
year: extractedData['Search'][i]['Year'],
));
}
Thanks to the Both of you I got it working.
Thank you so much

How to aggregate a MongoDB query to remove the usage of a for loop?

I am new to MongoDB and am working with it on NodeJS code.
As you can see the below code, I am running a for loop through my books collection to figure out the latest version of the query_book.
I know that this isn't efficient, and want to understand how can an aggregation function be written for it in MongoDB.
Current code:
let result= {};
_.forEach(books, function(query_book)
{
if(!result[query_book.book_id])
{
result[query_book.book_id] = query_book
}
else if(result[query_book.book_id].book_version <
query_book.book_version)
{
result[query_book.book_id] = query_book
}
Data Object for books:
[
{
"book_id": "ab12nld”,
"book_version": "0”,
"author": “Sam”,
“name”: “Sample Book”,
“comments”: “Done”
},
{
"book_id": "ab12nld”,
"book_version": "1",
"author": "Martin",
"name": "Sample Book",
“comments”: “In Progress”
},
{
"book_id": "ab12nld”,
"book_version": "2",
"author": "Roy",
"name": "Sample Book",
“comments”: “To-Do”
}
]
[
{
"book_id": "bcj123n”,
"book_version": "0”,
"author": “Don”,
“name”: “Another Book”,
“comments”: “Done”
},
{
"book_id": "bcj123n”,
"book_version": "1",
"author": "Ray",
"name": "Another Book",
“comments”: “In Progress”
},
{
"book_id": "bcj123n”,
"book_version": "2",
"author": "Max",
"name": "Another Book",
“comments”: “To-Do”
}
]
In this case, I want to fetch the object having the maximum value of book_version for my input book_id which is ab12nld:
{
"book_id": "ab12nld”,
"book_version": "2",
"author": "Roy",
"name": "Sample Book",
“comments”: “To-Do”
}
If using Node.js Mongo driver (replace [bookId] with your input)
db.collection('books')
.findOne({ book_id: [bookId] }, { sort: [['book_version', -1]] })
Or, if using Mongoose,
Book.findOne({ book_id: [bookId] }).sort({ book_version: -1 })
db.books.aggregate([{ "$sort": { "book_version": -1 } },{"$limit":1}])
I understood that you want to retrieve a document having highest value of a field.
Here, the document having the highest value in the version field. The simplest way of doing this is to sort in reverse order and get the first document.
You can also use the aggregate method.
Try the following snippet. I think it will help.
db.books.find().sort({"book_version":-1}).limit(1);

How to filter embedded array in mongo document with morphia

Given my Profile data looks like below, I want to find the profile for combination of userName and productId
and only return the profile with the respective contract for this product.
{
"firstName": "John",
"lastName": "Doe",
"userName": "john.doe#gmail.com",
"language": "NL",
"timeZone": "Europe/Amsterdam",
"contracts": [
{
"contractId": "DEMO1-CONTRACT",
"productId": "ticket-api",
"startDate": ISODate('2016-06-29T09:06:42.391Z'),
"roles": [
{
"name": "Manager",
"permissions": [
{
"activity": "ticket",
"permission": "createTicket"
},
{
"activity": "ticket",
"permission": "updateTicket"
},
{
"activity": "ticket",
"permission": "closeTicket"
}
]
}
]
},
{
"contractId": "DEMO2-CONTRACT",
"productId": "comment-api",
"startDate": ISODate('2016-06-29T10:27:45.899Z'),
"roles": [
{
"name": "Manager",
"permissions": [
{
"activity": "comment",
"permission": "createComment"
},
{
"activity": "comment",
"permission": "updateComment"
},
{
"activity": "comment",
"permission": "deleteComment"
}
]
}
]
}
]
}
I managed to find the solution how to do this from the command line. But I don't seem to find a way how to accomplish this with Morphia (latest version).
db.Profile.aggregate([
{ $match: {"userName": "john.doe#gmail.com"}},
{ $project: {
contracts: {$filter: {
input: '$contracts',
as: 'contract',
cond: {$eq: ['$$contract.productId', "ticket-api"]}
}}
}}
])
This is what I have so far. Any help is most appreciated
Query<Profile> matchQuery = getDatastore().createQuery(Profile.class).field(Profile._userName).equal(userName);
getDatastore()
.createAggregation(Profile.class)
.match(matchQuery)
.project(Projection.expression(??))
Note... meanwhile I found another solution which does not use an aggregation pipeline.
public Optional<Profile> findByUserNameAndContractQuery(String userName, String productId) {
DBObject contractQuery = BasicDBObjectBuilder.start(Contract._productId, productId).get();
Query<Profile> query =
getDatastore()
.createQuery(Profile.class)
.field(Profile._userName).equal(userName)
.filter(Profile._contracts + " elem", contractQuery)
.retrievedFields(true, Profile._contracts + ".$");
return Optional.ofNullable(query.get());
}
I finally found the best way (under assumption I only want to return max. 1 element from array) to filter embedded array.
db.Profile.aggregate([
{ $match: {"userName": "john.doe#gmail.com"}},
{ $unwind: "$contracts"},
{ $match: {"contracts.productId": "comment-api"}}
])
To match according to your first design you could try the projection settings with morphia aggregation pipeline.
Query<Profile> matchQuery = getDatastore().createQuery(Profile.class).field(Profile._userName).equal(userName);
getDatastore()
.createAggregation(Profile.class)
.match(matchQuery)
.project(Projection.expression("$filter", new BasicDBObject()
.append("input", "$contracts")
.append("as", "contract")
.append("cond", new BasicDBObject()
.append("$eq", Arrays.asList('$$contract.productId', "ticket-api")));
Also see the example written by the morphia crew around line 88 at https://github.com/mongodb/morphia/blob/master/morphia/src/test/java/org/mongodb/morphia/aggregation/AggregationTest.java.

Insert object in nested array mongodb nodejs

So I am trying to insert an object in parameters and have been unsuccessful. My mongodb structure looks like this:
[
{
"_id": "04",
"name": "test service 4",
"id": "04",
"version": "0.0.1",
"title": "testing",
"description": "test",
"protocol": "test",
"operations": [
{
"_id": "99",
"oName": "test op 52222222222",
"sid": "04",
"name": "test op 52222222222",
"oid": "99",
"parameters": {},
"description": "testing",
"returntype": "test"
},
{
"_id": "58",
"oName": "test op 52222222222",
"sid": "04",
"name": "test op 52222222222",
"oid": "58",
"parameters": {},
"description": "testing",
"returntype": "test"
}
]
}
]
I want to be able to add an object into parameters with basic details such as name, id, and type. I am not entirely sure how to tackle this as I have all other CRUD operations implemented up until the parameters part. How should I go about to complete this? I know mongodb has issues when trying to insert something into an array inside an array, so if anyone has any suggestions as to how I can complete this I would really appreciate it. Thanks.
One of the problems is I do not have access to the _id of the root object, but I do have the _id for the operation where I am inserting the parameter. Hence I was trying to insert the parameter using this code:
collection.update({"operations":{"$elemMatch": {"oid": oid}}}, {'$addToSet':{"operations.parameters": {name: "test"} }}, {safe:true}, function(err, result) {
if (err) {
res.send({'error':'An error has occurred'});
} else {
res.send(result[0]);
}
});
This does not work though.
I was able to complete the insert by using the following code:
collection.update({ "operations": {$elemMatch: {_id:oid}}}, {$addToSet: { "operations.$.parameters" : parameter}}, function(err, result) {
if (err) {
res.send({'error':'An error has occurred'});
} else {
res.send(result[0]);
}
});
Just in case anyone needed it.
This is because you need to use positional operator, The example I am copying from the link is almost the same as in your case:
db.students.update(
{ _id: 4, "grades.grade": 85 },
{ $set: { "grades.$.std" : 6 } }
)

Resources