My firestore document I'm trying to serialize contains a map of maps of strings like this:
Some example document in my collection:
id: "someId1" (String)
vouchers (Map)
voucher_with_some_random_id_1 (Map)
name: "name 1"
description: "description 1"
voucher_with_some_random_id_2 (Map)
name: "name 2"
description: "description 2"
The problem I have with creating a matching POJO class is that the field names of the inner maps are not constant since the amount of vouchers in my vouchers Map changes from time to time.
For documents with unknown custom IDs there is a solution (Annotation #DocumentId)
But I couldn't find an annotation that works for maps.
My latest failed attempt:
public class MyPOJO {
public MyPOJO() {
}
public String id;
public VouchersPOJO vouchers;
}
public class VouchersPOJO {
public VouchersPOJO() {
}
public List<ActualVoucherPOJO> vouchers;
}
public class ActualVoucherPOJO {
public ActualVoucherPOJO() {
}
public String name, description;
}
MyPOJO x = documentSnapshot.toObject(MyPOJO.class);
As you already say voucher is a Map, so that's what you should use in your Java class too:
public class MyPOJO {
public String id;
public Map<String,Voucher> vouchers;
}
public class Voucher {
public String name, description;
}
In this case, I don't think toObject() is going to work on the entire data structure. It's not meant for handling dynamic/variable data. Instead, you will have to manually:
Iterate the entries of the Map of Maps that come back from the snapshot
Convert each map to the individual objects
Assemble the final MyPOJO object using what you found in the maps
Related
I have following object:
public class Cart {
public String id;
public List<Map<Product, Double>> productsInCart;
}
In this key itself is a complex object, how to do it in firestore?
Also above object can be redesigned as following
public class Cart {
public String id;
public Map<Product, Double> productsInCart;
}
Firestore does not have any sense of "complex" keys or properties. Fields and nested object property names must be strings. If you need to store something more complex, you will need to somehow reduce it down to a unique string. One option is to use a hash of the data in the object, but you're better off redesigning your object to simply use strings as keys.
Hi all I can't think of a better example to illustrate my point so do let me know If my example has some errors. But hopefully this example will get my point through.
class A {
String CATEGORY = "A";
public String getCATEGORY() {
return CATEGORY;
}
}
class B extends A {
String CATEGORY = "B";
#Override
public String getCATEGORY() {
return CATEGORY;
}
}
class C extends A {
String CATEGORY = "C";
#Override
public String getCATEGORY() {
return CATEGORY;
}
}
public class MyClass {
private List<A> array = Arrays.asList(new A(), new B(), new C());
public MyClass() {}
}
Now if I upload MyClass onto firebase using setValue for example, firebase will show me the properties of class A, B and C. However, when I read the data from firebase and call sth like getValue(MyClass.class) the List it returns me are all of type A and the subclasses are not preserved. Is there a workaround to allow firebase to preserve the class types uploaded?
If you use Firebase's default serializer, it simply writes all public properties and fields to the database. Say that you store a single instance of each class, it'd be:
-L1234567890: {
cATEGORY: "A"
},
-L1234567891: {
cATEGORY: "B"
},
-L1234567892: {
cATEGORY: "C"
},
There won't be enough knowledge in the database for the SDK to reinflate the correct sub-class. While you and I can see that the cATEGORY value matches the class name, the Firebase SDK has no such knowledge.
It won't be too hard to write your own custom deserializer for this data though, taking a DataSnapshot with the values above and reinflating the correct class and values.
You could also do a hybrid: detect the class type directly, and then tell Firebase what class to read:
String cat = snapshot.child("cATEGORY").getValue(String.class)
Class clazz = "C".equals(cat) ? C.class : "B".equals(cat) ? B.class : A.clas;
A object = snapshot.getValue(clazz);
I am trying to access a getter method which is inside a POJO class within my response modal.
public class Event implements Parcelable{
class Name {
#SerializedName("text")
#Expose
public String eventName;
public String getEventName() {
return eventName;
}
}
.
. some parcelable stuff here
.
}
I am trying to access the getEventName method from within my adapter class. my piece of code there goes like this ( cant access the method, geteventname):
holder.cardTextView.setText(eventsList.get(position).getEventName());
If i define another variable outside of an inner pojo class, i can reach its getter, i can only not reach the one within the pojo class.
Edit
I am trying to read a json response like this, the text under name is the one im trying to build the modal for.
"events": [
{
"name": {
"text": "textextextext",
"html": "textextextext"
},
"description": {}
.
.
.
Thank you in advance.
JSON nesting is fun.
name is an inner class. Looks like description is also.
events":[ { "name":{ "text":" text ", "html":"something " }, "description":{ }, ... }]
In the above, there is an array of events.
Every event has a name, and every name has a "text" and an "html".
Every event also has a description, which has it's own fields.
You are on the right track:
public class Event implements Parcelable{
#SerializedName("name")
#Expose
public Name name;
#SerializedName("description")
#Expose
public Description description;
public class Name {
#SerializedName("text")
#Expose
public String eventName;
public String getEventName() {
return eventName;
}
}
public class Description {
//whatever fields are in the description object in the json
}
.
. some parcelable stuff here
.
}
You would access it like: holder.cardTextView.setText(eventsList.get(position).name.getEventName());
If this works you can clean it up by adding custom getters to the event class.
holder.cardTextView.setText(eventsList.get(position).getEventName());
try this. use parentheses for function call
Try this getEventName() with parentheses :
holder.cardTextView.setText(eventsList.get(position).getEventName());
I push data to Firebase using Order object, the question is I want the first letter of every child name capital. I defined the property like "Complain" but in Firebase it still shows as "complain", I dont know how to make it.
The current structure of the Firebase:
The structure I want:
I defined the property like this:
#Data
public class Order implements Serializable {
#SerializedName("Complain")
private String Complain;
public Order() {
Complain = "";
}
public String getComplain() {
return Complain;
}
public void setComplain(String complain) {
Complain = complain;
}
}
I push data to Firebase like this:
Map<String, Object> map = new HashMap<>();
map.put(orderSavePath, order);
reference.updateChildren(map).addOnCompleteListener(listener);
The Firebase JSON serialization name is controlled by the annotation PropertyName.
public class Order implements Serializable {
private String Complain;
public Order() {
Complain = "";
}
#PropertyName("Complain")
public String getComplain() {
return Complain;
}
#PropertyName("Complain")
public void setComplain(String complain) {
Complain = complain;
}
}
The annotation needs to be on both the getter and the setter. Alternatively you can just use public fields and reduce the class to:
public class Order {
#PropertyName("Complain")
public String Complain;
}
I have a base class that stores a field called updatedAt and a setter setUpdatedAt. There is a child class Child. I know that Firebase cannot find setter in the parent classes, so I just define the setter setUpdatedAt again in Child, but I am still getting "No setter/field for updatedAt found on class xxx.Child"
My database json has that "updatedAt" field.
Sample code:
public abstract class Model {
private String mId;
private long mUpdatedAt;
public String getId() {return mId;}
public void setId(String id) { mId = id;}
public long getUpdatedAt() {return mUpdatedAt;}
public void setUpdatedAt(long updatedAt) { mUpdatedAt = updatedAt;}
}
public abstract class Child extends Model {
private String mCreator;
public String getCreator() {
return mCreator;
}
public void setCreator(String creator) {
mCreator = creator;
}
// having the following or not does not change the outcome
public void setUpdatedAt(long updatedAt) {
super.setUpdatedAt(updatedAt);
}
}
// then somewhere else, do this
new ValueEventListener() {
public void onDataChange(DataSnapshot dataSnapshot) {
model = dataSnapshot.getValue(Child.class);
}
And I got "No setter/field for updatedAt found on class xxx.Child" and "No setter/field for id found on class xxx.Child" when "model = dataSnapshot.getValue(Child.class);" is executed.
For actual code sample, see github MinFirebaseApp. In MainActivity, you can see the two methods "createEmployee" and "showEmployee", which shows how an employee is created in "createEmployee" and the same employee is fetched in "showEmployee". Using the code, you can reproduce it by (after you change the package id to yours):
Put the google-services.json file in the app folder
Start the app
Click on "login" to sign in anonymously
Click on "create employee", see that the employee is created
Go to console.firebase.google.com to check that the employee is
created with id, name and updatedAt
Click on "show employee" from the app, see the "updatedAt" and "id"
are not set.
Check the logcat to find something like "No setter/field for id found" and "No setter/field for updatedAt found"
You'll need to call through to all of your setters in the subclass. You may as well do the getters too. Right now you're just doing setUpdatedAt(), but there is setId() as well. This is a bug in the Android client library for Realtime Database, but the team is working on a fix that will be available in the future.