I have list of object store in single document in Firestore. On my android side i am not able to fetch list of object and their inner data.
Here is code i am trying to fetch data
CollectionReference mCollectionReference = FirebaseFirestore.getInstance().collection("Categories");
mCollectionReference.document("Dinner Dishes").get().addOnSuccessListener(new OnSuccessListener<DocumentSnapshot>() {
#Override
public void onSuccess(DocumentSnapshot documentSnapshot) {
Collection<Object> objectMap = documentSnapshot.getData().values();
for(Object result:objectMap){
Sample obj = Sample.class.cast(result);
HashMap<String, String> pic = obj.getPic();
HashMap<String, String> name = obj.getName();
}
}
});
I need list of object i upload on Firestore in single document
My Model Class:
public class Sample {
private String name;
private String pic;
public Sample() {
}
public String getName() {
return name;
}
public String getPic() {
return pic;
}
}
You can convert it to a java object, if that is how you saved it?
ObjectType myObject = document.toObject(ObjectType.class);
Related
I have the below structure in my Firestore and I want to read data and store it in ArrayList like I have "amountArrayList" which will read data from the "transactions" field in Firestore, I want to read all the "amount" fields from "transactions" field and make array list of it so that I can show it in list manner.
My code
Map<String, Object> map = document.getData();
for (Map.Entry<String, Object> entry : map.entrySet()) {
if (entry.getKey().equals("transactions")) {
System.out.println(entry.getValue().toString());
}
}
Output
[{transactionType=Credit, amount=3000, dateToStr=17/12/2021, timeToStr=08:06:10, description=}, {transactionType=Credit, amount=2000, dateToStr=17/12/2021, timeToStr=08:06:50, description=}]
Since transactions is an array field, the value you get from entry.getValue() is a List of objects. Since each of these objects in the JSON has properties, they each will be a Map<String, Object> again in the Java code.
A simple way to print the amounts would be something like:
List transactions = document.get("transactions");
for (Object transaction: transactions) {
Map values = (Map)transaction;
System.out.println(values.get("amount")
}
While Frank van Puffelen's answer will work perfectly fine, there is a solution in which you can directly map the "transactions" array into a list of custom objects. Assuming that you have a class declaration that looks like this:
class User {
public String balance, email, firstname, lastname, password, username;
public List<Transaction> transactions;
public User(String balance, String email, String firstname, String lastname, String password, String username, List<Transaction> transactions) {
this.balance = balance;
this.email = email;
this.firstname = firstname;
this.lastname = lastname;
this.password = password;
this.username = username;
this.transactions = transactions;
}
}
And one that looks like this:
class Transaction {
public String amount, dateToStr, description, timeToStr, transactionType;
public Transaction(String amount, String dateToStr, String description, String timeToStr, String transactionType) {
this.amount = amount;
this.dateToStr = dateToStr;
this.description = description;
this.timeToStr = timeToStr;
this.transactionType = transactionType;
}
}
To get the list, it will be as simple as:
docRef.get().addOnCompleteListener(task -> {
if (task.isSuccessful()) {
DocumentSnapshot document = task.getResult();
if (document.exists()) {
List<Transaction> transactions = document.toObject(User.class).transactions;
List<String> amountArrayList = new ArrayList<>();
for(Transaction transaction : transactions) {
String amount = transaction.amount;
amountArrayList.add(amount);
}
// Do what you need to do with your amountArrayList
}
}
});
You can read more info in the following article:
How to map an array of objects from Cloud Firestore to a List of objects?.
I found the best way to map the data from/to hashmap to send/retrieve it from FirebaseFirestore
First, you need to create an extension class to handle the mapping
fun <T> T.serializeToMap(): Map<String, Any> {
return convert()
}
inline fun <reified T> Map<String, Any>.toDataClass(): T = convert()
inline fun <A, reified B> A.convert(): B =
Gson().fromJson(Gson().toJson(this), object : TypeToken<B>() {}.type)
Don't forget to add Gson
implementation 'com.google.code.gson:gson:2.9.0'
Then, you can use these functions for mapping the result from firebase
if (task.isSuccessful) {
val list: MutableList<YourObject> = ArrayList()
for (document in task.result) {
list.add(document.data.toDataClass())
}
// do whatever you want with the list :)
} else {
// handle the error
}
I have a Java customobject that I use in Firestore to set a product.
It looks partly like this:
package product.firebaseobjects;
import java.util.List;
import java.util.Map;
public class Product {
private List<String> productcodes;
private Map<String, String> altdescriptions;
private String description;
private Long amount;
private boolean hasimage;
public Product(){
}
public FBProduct(List<String> productcodes, Map<String, String> altdescriptions, String description, Long amount, boolean hasimage) {
this.productcodes = productcodes;
this.altdescriptions = altdescriptions;
this.description = description;
this.amount = amount;
this.hasimage = hasimage;
}
(getters & setters omitted)
Retrieving is done using
DocumentReference docRef = db.collection(PRODUCTS).document(id);
ApiFuture<DocumentSnapshot> future = docRef.get();
DocumentSnapshot document = null;
Product product = null;
try {
document = future.get();
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
if (document.exists()) {
product = document.toObject(Product.class);
}
return product;
When I save this object using this code
List<string> productcodes = new ArrayList<>();
productcodes.add("productcode1");
productcodes.add("productcode2");
Map<String, String> alt = new HashMap<>();
alt.put("French","French description");
alt.put("German","German description");
String description = "Description";
Long amount = 1L;
boolean hasimage = false;
Product product = new Product(productcodes, alt, description, amount, hasimage);
ApiFuture<DocumentReference> future = db.collection(PRODUCTS).add(product);
all fields are saved to Firestore except for the field 'altdescriptions'. This field is not to be found. All examples in the docs use Map<String, Object> which i tried as wel but does not make a difference.
From searching I found solutions that use the update() method using dot notation, but then you are not using the customobject.
My question is, how to use a Map in a Firestore Java customobject. And if that is not possible, what is the preferred way of setting, updating and retrieving Maps for a customobject.
I know there's a lot of similar questions like mine, but I've read and tried everything but still unable to solve this issue. What I am trying to achieve is to populate my listview with the data from the firebase. Im following a tutorial on youtube but Ive added some stuff, timestamp in particular. The error is in my for loop and says :
Class java.util.Map has generic type parameters, please use GenericTypeIndicator instead
This is my database looks like :
My Notes.java
public class Notes {
String noteId;
String noteCategory;
String note;
String rating;
public Map timeStamp;
public Notes(){
}
public Notes(String noteId, String noteCategory, String note, String rating, Map timeStamp) {
this.noteId = noteId;
this.noteCategory = noteCategory;
this.note = note;
this.rating = rating;
this.timeStamp = timeStamp;
}
public String getNoteId() {
return noteId;
}
public String getNoteCategory() {
return noteCategory;
}
public String getNote() {
return note;
}
public String getRating() {
return rating;
}
public Map<String,String> getTimeStamp() { return timeStamp;}
}
And below is my NoteList.java
#Override
protected void onStart() {
super.onStart();
databaseNotes.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
notesList.clear();
for(DataSnapshot noteSnapshot : dataSnapshot.getChildren()){
Notes notes = noteSnapshot.getValue(Notes.class);
notesList.add(notes);
}
NoteList adapter = new NoteList(MyNotes.this, notesList);
listViewNotes.setAdapter(adapter);
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
Any advice would be greatly appreciated! Thank you in advance.
EDIT : Okay, Ive tried the suggested answer, and after I logged the map it shows my data on the console. However, how do I iterate my map into the listview ?
Map<String, Object> map = (Map<String, Object>) dataSnapshot.getValue();
From looking at your DB, it looks like you should use int or long type instead of the map type, since your timestamp has a number value
I'm unable to retrieve the data which is nested inside "Leagues" and "Season".
In the database below, I cannot retrieve Season 2016/2017 points and results.
I am able to access the data that is not nested, such as Email and Username without problems
"Users" :
"User1" : {
"Email" : "dfs#sdf.com",
"Last_login" : "5:15pm",
"Username" : "Test",
"Leagues" : {
"FootballLeague" : true,
"CricketLeague" : true
},
"Season" : {
"2017" : {
"Points" : 5,
"Results" : 2
"newdata" : {
"randomdata1: data1",
"randomdata2: data2"
},
}
"2018" : {
"Points" : 7,
"Results" : 2
}
}
The following class is what I'm using to store data as objects:
public class Users {
private String Username;
private String Email;
private String Last_login;
private Map<String, Boolean> Leagues;
private Map<String, thisSeason> Season;
public Users() {
}
//All getters and setters for the strings.
public Map<String, Boolean> get_joined_Leagues() {
return Leagues;
}
public void set_joined_Leagues(Map<String, Boolean> leagues) {
Leagues = leagues;
}
public Map<String, thisSeason> getSeason() {
return Season;
}
public void set_thisSeason(Map<String, thisSeason> season) {
Season = season;
}
public static class thisSeason {
private int Points;
private int Results;
private Map <String, thisNewData> newdata;
public thisSeason() {
}
public int getthisSeason_results() {
return Results;
}
public void setthisSeason_results(int resultsin) {
Results = resultsin;
}
public int getthisSeason_points() {
return Points;
}
public void setSeason_points(int Pointsin) {
Points = Pointsin;
}
public Map<String, thisNewData> getNewData() {
return newdata;
}
public void set_thisNewData(Map<String, thisNewData> newdata_in) {
newdata= newdata_in;
}
public static class thisNewData {
private String randomdata1;
private String randomdata2;
//Getters and Setters
}
Here is part of my Java class where I access the Database:
List<Users> usersList= new ArrayList<Users>();
DatabaseReference fbDB = FirebaseDatabase.getInstance().getReference();
fbDB.child("Users").addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
if (dataSnapshot.exists()) {
for (DataSnapshot ds : dataSnapshot.getChildren()) {
Users C = ds.getValue(Users.class);
usersList.add(C);
System.out.println(C.getSeason().getthisSeason_points()); //ERROR occurs here - null object reference
}
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
}
I get a null object reference error as shown in the code above.
Also, on a slight side note - I'm aware my implementation for getting Leagues is incorrect. How can I retrieve the keys instead?
The main problems that I found in your code:
First, your Leagues object:
Well, it should not be a custom object at all, if it stays in the current structure, but a Map. This is the map object - a collection which has a key, and a value, just like a child in the Realtime Database.
If a league name (=key) didn't have a true (=value) next to it - the names could have been stored in String variables, but this is not the case, because every child in the Realtime Database must have a value, and storing a "dummy" value next to a league name makes each league a part of a Map. This current "dummy" is a boolean, and that's why this will be the leagues' data type:Map<String, Boolean> leagues.
Second, your Season object:
You are confusing between one season and many seasons. Your child - "Season" actually stores a lot of seasons. Thats why this case will be similar to the one above - each season's time is the key and the season itself is the value.
Therefore, the User class's variables should be:
private String Username;
private String Email;
private String Last_login;
private Map<String,Boolean> Leagues;
private Map<String, Season> Seasons;
I'm receiving these errors when I tried to populate a list view with firebase adapter using firebase UI
com.google.firebase.database.DatabaseException: Failed to convert
value of type java.util.HashMap to String
com.google.firebase.database.DatabaseException: Class
java.util.HashMap has generic type parameters, please use
GenericTypeIndicator instead
Here Is the code
DatabaseReference ref = FirebaseDatabase.getInstance().getReference();
DatabaseReference a = ref.child("info");
final FirebaseListAdapter<String> adapter =
new FirebaseListAdapter<String>(this,String.class,android.R.layout.simple_list_item_1,a) {
#Override
protected void populateView(View v, String model, int position) {
TextView text = (TextView) findViewById(android.R.id.text1);
text.setText(model);
}
here is an example of json data
"info" : {
"KSTUJILdwPN305Fs7ujhga4knlG3" : {
"File Info" : {
"-JFRkA855rfOU7GtcK4" : {
"Name" : "John",
"Adress" : "Test Adress",
"Favourite_food" : "Bread",
},
info node does not refer to your data model. It may contain children nodes. So to reach the model, you should use a reference like this.
DatabaseReference a = ref.child("info").child(info_id).child("File Info").child(file_id);
and you should have a FileInfo model instead of String model to use in
populateView(View v, FileInfo model, int position):
and model
public class FileInfo {
private String Name;
private String Adress;
private String Favourite_food;
public FileInfo() {
}
public FileInfo(String Name, String Adress, String Favourite_food) {
this.Name = Name;
this.Adress = Adress;
this.Favourite_food = Favourite_food;
}
public String getName() {
return Name;
}
public void setName(String Name) {
this.Name = Name;
}
public String getAdress() {
return Adress;
}
public void setAdress(String Adress) {
this.Adress = Adress;
}
public String getFavourite_food() {
return Favourite_food;
}
public void setFavourite_food(String Favourite_food) {
this.Favourite_food = Favourite_food;
}
}
final FirebaseListAdapter<String> adapter = new FirebaseListAdapter<String>(this,String.class,android.R.layout.simple_list_item_1,a)
public FirebaseListAdapter(Activity activity,
java.lang.Class<T> modelClass,
int modelLayout,
com.firebase.client.Firebase ref)
modelClass - Firebase will marshall the data at a location into an instance of a class that you provide
replace the constructor's 2nd parameter (String.class) with a POJO with the fields from info (name, address, favouriteFood)
FirebaseListAdapter
Edit:
You should also use getters in the populateView method