Can you explain this dart json parsing code please? - android

So i have this code that works somehow, however i can't really explain why.
To get this code i used an automatic json to code application called quicktype using as input a json that my company provides. The goal of the code is obtaining a Map containing polygons in this format : {(Random ID of polygon)} ==> [Lat,Lon]
class Poligoni {
Map<String, List<String>> poligoni;
Poligoni({
this.poligoni,
});
factory Poligoni.fromRawJson(String str) {
return Poligoni.fromJson(json.decode(str));
}
factory Poligoni.fromJson(Map<String, dynamic> json) {
return Poligoni(
poligoni: Map.from(json["Poligoni"]).map((k, v) {
return MapEntry<String, List<String>>(
k, List<String>.from(v.map((x) => x)));
}),
);
}
}
So the code either accepts a raw json from the (.fromRawJson) function or a decoded one from (.fromJson) function. No problems here. The part that confuses me is this one :
factory Poligoni.fromJson(Map<String, dynamic> json) {
return Poligoni(
poligoni: Map.from(json["Poligoni"]).map((k, v) {
return MapEntry<String, List<String>>(
k, List<String>.from(v.map((x) => x)));
}),
);
}
The problems i have understanding this are(.fromJson function):
Why i return a Class? example: return Poligoni(.....various code)
The use of the redirecting constructor in not very clear in this case to me poligoni:
A lot of difficulty undestanding the function after poligoni: : i know that the function parses the json key 'Poligoni' and transform this in another map of type <String>,List<String>
that contains for every key a list of points which form a polygon however i am not sure how, if you can write a step-by-step walk through it will be greatly appreciated or in alternative if you can reformat this code to make it clearer.
I tried looking through dart and flutter docs but i didn't unterstand most of it because of the nested functions that my code uses and a little bit of language barrier.
Any help is greatly aprreciated,
Best Regards.

Why i return a Class? example: return Poligoni(.....various code)
Because you are using a factory constructor. A factory constructor is like a static method but your are required to return an instance of an object that are compatible with the class it is part of.
One advantage of this pattern is that when using a normal constructor, you are not allowed to set any final variables inside the constructor body but needs to set them as part of the initializing phase.
In you case, this is really not a problem since the variable Map<String, List<String>> poligoni is not final but it can still be a nice pattern.
Read more about factory constructors here:
https://dart.dev/guides/language/language-tour#factory-constructors
The use of the redirecting constructor in not very clear in this case to me poligoni:
See my previous answer.
A lot of difficulty undestanding the function after poligoni: : i know that the function parses the json key 'Poligoni' and transform this in another map of type ,List that contains for every key a list of points which form a polygon however i am not sure how, if you can write a step-by-step walk through it will be greatly appreciated or in alternative if you can reformat this code to make it clearer.
factory Poligoni.fromJson(Map<String, dynamic> json) {
return Poligoni(
poligoni: Map.from(json["Poligoni"]).map((k, v) {
return MapEntry<String, List<String>>(
k, List<String>.from(v.map((x) => x)));
}),
);
}
As previous stated we have defined a factory constructor which must return an object which are compatible with the class it is part of. In this case we therefore creates a new Poligoni object by calling the constructor of Poligoni which takes a named argument poligoni. This is why we have poligoni:.
We are setting the named argument poligoni to the value of the following statement:
Map.from(json["Poligoni"]).map((k, v) {
return MapEntry<String, List<String>>(
k, List<String>.from(v.map((x) => x)));
})
I am not really sure why we are using Map.from but the purpose of this constructor is to create a new map based on the key-values from another map:
https://api.dart.dev/stable/2.10.4/dart-core/Map/Map.from.html
(personally, I would have used a typecast from json["Poligoni"] since this must be a Map instance)
We are then calling .map on this new Map to create another new Map but where each key-value pair is mapped to new key-value pairs by the following method:
(k, v) {
return MapEntry<String, List<String>>(
k, List<String>.from(v.map((x) => x)));
}
We can see here we are keeping the original key k but are converting the value v with:
List<String>.from(v.map((x) => x))
My guess here is that v is List<dynamic> and we want to create a new list of the type List<String> by taking each element of List v and inserting it into a new List which has been defined as List<String> from the start.
The result of this is then used as value in our new Map which are then used as value for the poligoni argument.
In short, all of this operations are really used for making your code more type safe by converting dynamic types into statically determined types.

Related

Passing a function that returns an object in Dart / Flutter does not work

I'm pretty new in Dart coding world.
I'm using Sembast (ver 2.1.1) and coding in Flutter.
I've created an abstract class called ImageItem. Then other types of images inherit from it, say a ArtworkItem class:
class ArtworkItem extends ImageItem {
DateTime lastUpdated;
String category;
ArtworkItem({
this.lastUpdated,
this.category,
);
static ArtworkItem fromMap(Map<String, dynamic> map) {
return ArtworkItem(
lastUpdated = map['last_updated'],
category = map['category'],
);
}
}
So in the code above the ArtworkItem has a static method that returns the ArtworkItem object from a map.
Then there is a class called Repository. Repository has a method that has takes a Function as a parameter, and it returns a List. Like this:
List<ImageItem> getImagesFromRepository(ImageItem Function(Map<String, dynamic) fromMap) {
// Do something here to get List of RecordSnapshot object.
// ....Additional code here
final recordSnapshots = await _store.find( await database, finder: finder, );
return recordSnapshot.map((snapshot) => fromMap(snapshot.value));
}
And when I call this method like this in a DAO file:
return getImagesFromRepository(ArtworkItem.fromMap);
I got and Exception:
'MappedListIterable<SembastRecordSnapshot<String, Map<String, dynamic>>, ImageItem>' is not a subtype of type 'List<ImageItem>'
But if I do:
return recordSnapshots
.map(
(snapshot) => ArtworkItem.fromMap(snapshot.value))
.toList();
This works, just by specifying explicitly ArtworkItem.fromMap it works. But this will defeat the purpose of abstraction.
I'll just make a wild guess that your callback isn't explicitly typed.
List<ImageItem> getImagesFromRepository(ImageItem Function(Map<String, dynamic>) fromMap) {}
DateTime is not a supported type in sembast (it uses json which does not support DateTime). lastUpdated should/could be converted to an int (millis since epoch) or an ISO8601 string.
I don't if that's the reason of your issue (I'm assuming recordSnapshot is of type List, not RecordSnapshot) and why you did not get any error before. It is hard to say whether your app is stalled as you said or whether an exception was thrown, you could try to wrap into a try/catch and print the error for debugging purpose.
#zk_regen, i think your getImagesFromRepository function is expecting incorrect parameter. I think it should expect ImageItem if you're passing an instance of ArtworkItem like so,
List<ImageItem> getImagesFromRepository(ImageItem imageItem) {
// Do something here to get List of RecordSnapshot object.

Call Firebase Cloud Functions Android

Hi to everyone watching this post.
I have my app, and I want to know how to call my Firebase Cloud Functions in it. I've been reading from the Firebase guides but I am really struggling to understand how it works.
I have a function where I want to create parties, and I have some values to be inserted such as address, date, owner, etc.
If anyone who knows about this can help me I would be really grateful, I can provide any more information that you could need. Thanks!
As Frank said, it is better when you ask a question on StackOveflow to include all the code you have already written.
However, from your comment, I understand you are referring to the code snippet that is in the documentation (copied/pasted below), and that you have problems with data.put.
private Task<String> addMessage(String text) {
// Create the arguments to the callable function.
Map<String, Object> data = new HashMap<>();
data.put("text", text);
data.put("push", true);
return mFunctions
.getHttpsCallable("addMessage")
.call(data)
.continueWith(new Continuation<HttpsCallableResult, String>() {
#Override
public String then(#NonNull Task<HttpsCallableResult> task) throws Exception {
// This continuation runs on either success or failure, but if the task
// has failed then getResult() will throw an Exception which will be
// propagated down.
String result = (String) task.getResult().getData();
return result;
}
});
}
This Java code snippet shows that the data that is passed (sent) to the Callable Cloud Function is contained in an HashMap, named data.
You will find a lot of tutorials on the web on how to use an HashMap, but in a nutshell:
"A Java HashMap is a hash table based implementation of Java’s Map interface. A Map, as you might know, is a collection of key-value pairs. It maps keys to values." Source: https://www.callicoder.com/java-hashmap/
A way to add new key-value pairs to the HashMap is by using the put() method. So this part of the code in the snippet is about adding data to the HashMap that will be sent to the CloudFunction.
And in the Cloud Function you will get this data as follows (as explained in the doc):
exports.addMessage = functions.https.onCall((data, context) => {
// ...
const text = data.text;
const push = data.push;
// ...
});

Firebase toObject with ref

With cloud firestore, you can convert a document to your object with document.toObject(YourClass.class); where the class's variables match those in your database. However, if one of the variables in the database is a reference, which data type would you use in the java class? Note that I need to not only store it in my data model but retrieve it and set it with the override methods of the form:
protected MyDataModel(Parcel in) {
mString = in.readString();
}
and
#Override
public void writeToParcel(Parcel parcel, int i) {
parcel.writeString(mString);
}
If I use a DocumentReference, then in writeToParcel(), how would I write it? Also, how would I read it in the protected Parcel in constructor? I have tried writeSerializable() and readSerializable() but DocumentReference does not implement Serializable.
I haven't used the Java API yet but I imagine you'll want to use the DocumentReference type. Here are some links in the firebase documentation:
DocumentReference
CollectionReference
getDocumentReference
If you need to serialize a DocumentReference, use its getPath() method to get a string that describes the document's location in the database. To deserialize that path string back into a DocumentReference, use FirebaseFirestore.getInstance().document(path).
When you have conversions like this to make, sometimes it more convenient to write all the code yourself rather than using the built-in object serialization provided by the Firestore SDK.
I had the same problem with document.toObject(YourClass.class);
I have an Event class, this class contains the id (document id) and a reference to another collection subCategoryReference.
I have created simples converters on my Event
For example:
val event = document
.toObject(Event::class.java)
.withId<Event>(document.id)
.withReference<Event>((document.data["subCategoryReference"] as DocumentReference).path)
In my Event class as you guessed I have two function withId and withReference
that look like this:
fun <T : Event?> withId(id: String): T {
this.id = id
return this as T
}
fun <T : Event?> withReference(reference: String): T {
this.subCategoryRef = reference
return this as T
}
Your class variables must have different names from your firestore variables/fields (just the variables you want to apply the converter on) - see the pic below
In my case on firestore, the reference field (variable) called subCategoryReference and in the Event class the variable name is subCategoryRef.
Let me know if you didn't get me.
I hope it helps...
Firestore console
Event Class

How can I overwrite the data in an element of an array of structures?

So, I have this structure:
typedef struct{
int serialNumber;
char name[100];
float price;
int quantity;
}Products;
And I created an array of structures dynamically.
The task was to 'simulate' a grocery store, with the user able to add and edit the items sold by the store. The following code snippets are for editing structure data.
void overwrite(Products store){
printf("Enter new serial number: ");
scanf("%d", &(store.serialNumber));
getchar();
printf("Enter new product name: ");
fgets(store.name, 100, stdin);
store.name[strlen(store.name)-1]='\0';
printf("Enter new product price: ");
scanf("%f", &(store.price));
printf("Enter new product quantity: ");
scanf("%d", &(store.quantity));
}
void editData(Products *store, int storeCapacity){ //storeCapacity needed to invoke printData(), assume a working code for the function.
int choice;
printData(store, storeCapacity);
printf("Enter slot number of product here: ");
scanf("%d", &choice);
overwrite(store[choice]);
}
Here's the catch, even though this code works, when I try to print the data, the data displays the values which should be overwritten. Have I forgotten to do something? I wish you could help me.
BTW, I code on an Android phone.
void overwrite(Products store){
C is pass by value, you need to pass a pointer to Products (i.e., Products *store) and modify the overwrite call in editData accordingly.
Basically the problem is that in C you pass arguments by value. So when you specify this signature
void overwrite(Products store)
and you invoke it somewhere:
Products myStore;
overwrite(myStore);
what happens is that a copy of myStore is created and placed on the stack, then the value is passed to the function. This means that every modification done to the Products object inside overwrite applies on the passed copy, not on the original object. This copy is then discarded when exiting the scope of overwrite function.
To solve this problem you must pass a pointer to the object, that is passed by value but being an address will point to the exact same myStore object. This is done in this way:
void overwrite(Products* store)
{
..
scanf("%f", &store->price);
..
}
Products myStore;
overwrite(&myStore);
According to Ouah, I passed the structure as the value itself, which did happen in my code.
So what I did is...
void overwrite(Products * store){ //formal parameter changed into a pointer
//codes here
}
And...
overwrite(&(store[choice])); /* actual parameter changed into a pointer by affixing ampersand*/
Further explanations of the codes' misbehavior were explained by Jack. I extend my gratitudes to you. The code now worked as it should be.

How to fetch object and array fields with Parse?

I'm unable to properly fetch a ParseObject that contains a field of type 'Object' : after changing manually the 'Object' field value in the Parse DataBrowser and then fetch the ParseObject from the app, the fetched ParseObject still provide the old value for the 'Object' field, but provide the right new value for the 'String' field.
Here is the sample code I use :
public class MainActivity extends ActionBarActivity {
ParseObject object;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
object = ParseObject.createWithoutData("Test", "tvgTg8jAXz");
}
#Override
protected void onResume() {
super.onResume();
object.fetchInBackground().onSuccess(new Continuation<ParseObject, Object>() {
#Override
public Object then(Task<ParseObject> task) throws Exception {
JSONObject data = task.getResult().getJSONObject("data");
String name = task.getResult().getString("name");
Log.d("OBJECT", data.toString());
Log.d("OBJECT", name);
return null;
}
}).continueWith(new Continuation<Object, Object>() {
#Override
public Object then(Task<Object> task) throws Exception {
if (task.getError() != null) {
Log.e("OBJECT", task.getError().getLocalizedMessage());
}
return null;
}
});
}
}
After I change both 'data' and 'name' fields in the DataBrowser, if 'onResume()' is called without a previous call to 'onCreate()' (after locking/unlocking screen for example) then the logs shows the old value for 'data' and the new value for 'name'.
This is a simple code example to highlight the problem I encounter in a bigger project.
Is this a known issue of the Parse Android SDK ? Is there a workaround ?
Thanks
Now that I learned that you have turned on the local datastore I can come with an, at least partial, answer.
Turning on the local datastore has some side effects. One being that only one instance of each object exists locally. So when you call fetchInBackground the second time, object is already populated with data. The problem then (i think) is that the API no longer override 'complex' types (pointers, objects, arrays), perhaps because it could mess up internal relationships in the data store. Since the fact that the data store will recursively save an object (and pointers) so suddenly swapping a pointer might leave objects 'hanging'. (again, only guessing).
Now I must admit that it still confuses me a bit looking at your code, cause it does not seem that you at any point write your object to the data store, however..
What should work is to unpin the object before 'refreshing' it:
object.unpinInBackground.onSuccess(new Continuation<>{
...
// when done call fetch
});
According to Parse, this is a known issue that they will not fix for now : https://developers.facebook.com/bugs/1624269784474093/
We must use the following methods to retrieve JSON objects/arrays fields from a ParseObject :
getMap() instead of getJSONObject()
getList() instead of getJSONArray()
These methods will return Map and List objects respectively.
I found that managing Map and List in my project instead of JSONObjet and JSONArray is not a problem and is even clearer.

Categories

Resources