I want to get all list of restaurants from Firebase in android.
Here is my code:
boolean delivery;
String openTime, closeTime, restaurantName;
long
There are two problems in your code. The first one would be this error:
Expected a List while deserializing, but got a class java.util.HashMap
Which can be solved using the following lines of code:
ValueEventListener valueEventListener = new ValueEventListener() {
@Override
public void onDataChange(@NonNull DataSnapshot dataSnapshot) {
List<RestaurantModel> listRes = new ArrayList<>();
for (DataSnapshot dataValues : dataSnapshot.getChildren()){
RestaurantModel restaurantModel = dataValues.getValue(RestaurantModel.class);
listRes.add(restaurantModel);
}
//Do what you need to do with listRes
}
@Override
public void onCancelled(@NonNull DatabaseError databaseError) {
throw databaseError.toException(); //Don't ignore errors
}
};
dataResReference.addListenerForSingleValueEvent(valueEventListener);
And the second problem is that you cannot return something now that hasn't been loaded yet. Firebase APIs are asynchronous
, meaning that onDataChange()
method returns immediately after it's invoked, and the callback from the Task it returns will be called sometime later. There are no guarantees about how long it will take. So it may take from a few hundred milliseconds to a few seconds before that data is available. Because that method returns immediately, your listRes
list you're trying to return, will be empty due to the asynchronous behavior of this method.
Basically, you're trying to return a value synchronously from an API that's asynchronous. That's not a good idea. You should handle the APIs asynchronously as intended.
A quick solution to this problem would be to use the listRes
list only inside the onDataChange()
method (as in the above lines of code), otherwise I recommend you see the last part of my answer from this post in which I have explained how it can be done using a custom callback. You can also take a look at this video for a better understanding.
Edit:
public void getAllRestaurant(){
ValueEventListener valueEventListener = new ValueEventListener() {
@Override
public void onDataChange(@NonNull DataSnapshot dataSnapshot) {
List<RestaurantModel> listRestaurant = new ArrayList<>();
for (DataSnapshot valueRes : dataSnapshot.getChildren()){
RestaurantModel restaurantModel = valueRes.getValue(RestaurantModel.class);
Log.d("Test", restaurantModel.getRestaurantName());
listRestaurant.add(restaurantModel);
}
//Do what you need to do with listRes
}
@Override
public void onCancelled(@NonNull DatabaseError databaseError) {
throw databaseError.toException();
}
};
DatabaseReference rootRef = FirebaseDatabase.getInstance().getReference();
rootRef.child("restaurants").addListenerForSingleValueEvent(valueEventListener);
}
I know this can be too late, but I wish my answer could help somebody. I know 2 ways to solve this problem. Once I have time I'll update my answer .
#Solution 1
First of all I copy & paste your code. It is not clear for me how did you add data to firebase, but I feel you added data directly to firebase.
so this is My MainActivity code :
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
RestaurantModel rm;
List<String> utils;
//restaurant 1
rm = new RestaurantModel();
rm.closeTime = "12:00am";
rm.openTime = "12:00pm";
rm.delivery = true;
rm.likes = 300;
rm.restaurantName = "KFC";
utils = new ArrayList<>();
utils.add("free wifi");
utils.add("free Parking");
utils.add("free water");
rm.utilities = utils;
//write first restaurant to Firebase
rm.addRestaurantToFirebase(rm);
//restaurant 2
rm = new RestaurantModel();
rm.closeTime = "5:00am";
rm.openTime = "12:00pm";
rm.delivery = false;
rm.likes = 500;
rm.restaurantName = "SubWay";
utils = new ArrayList<>();
utils.add("free wifi");
utils.add("free Parking");
rm.utilities = utils;
//write Second restaurant to Firebase
rm.addRestaurantToFirebase(rm);
rm.getallRestaurant();
}
}
and this is RestaurantModel :
public class RestaurantModel {
public boolean delivery;
public String openTime;
public String closeTime;
public String restaurantName;
public long likes;
public List<String> utilities;
public List<RestaurantModel> listRes;
DatabaseReference dataResReference;
public RestaurantModel() {
dataResReference = FirebaseDatabase.getInstance().getReference().child("restaurants");
}
public List<RestaurantModel> getallRestaurant() {
listRes = new ArrayList<>();
ValueEventListener valueEventListener = new ValueEventListener() {
@Override
public void onDataChange(@NonNull DataSnapshot dataSnapshot) {
for (DataSnapshot dataValues : dataSnapshot.getChildren()) {
RestaurantModel restaurantModel = dataValues.getValue(RestaurantModel.class);
listRes.add(restaurantModel);
Log.d("restaurantModel", restaurantModel.restaurantName);
}
}
@Override
public void onCancelled(@NonNull DatabaseError databaseError) {
}
};
dataResReference.addListenerForSingleValueEvent(valueEventListener);
return listRes;
}
public void addRestaurantToFirebase(RestaurantModel rm) {
dataResReference.child(restaurantName).setValue(rm);
}
}
here the result in firebase
Pre-requisit: We are setting the Snapshot response to our model class objects.
In our Model class. there must be one or more objects which we are setting as list.
Now in response, the ArrayList inside the Snapshot is coming to us as hashmap but we need list.
So the Solution i find is:
set the first layer of data to model objects as it is using limited parameter constructor.
Then the ArrayList object - Get it as a separate object of that specific class and then add it into the model class by getter.
Follow the below code sample.
Lets suppose you have Object Model class. And InnterList as an array list. My solution is to take innerList as a seperate object and then add it to model class using constructors.
If this list is not comming in response. You can handle them using limited constructor parameters.
Object object;
FirebaseDatabase.getInstance().getReference().getRoot().child("yourKey").addListenerForSingleValueEvent(new ValueEventListener() {
@Override
public void onDataChange(@NonNull DataSnapshot snapshot) {
for (DataSnapshot temp : snapshot.getChildren()) {
if (temp.hasChild("InnerList")) {
List<InnerList> innerList = new ArrayList<InnerList>();
DataSnapshot yourInnerArrayListSnapShot = temp.child("InnerList");
for (DataSnapshot innerTemp : yourInnerArrayListSnapShot.getChildren()) {
InnerList yourInnerArrayListObject = innerTemp.getValue(InnerList.class);
innerList.add(yourInnerArrayListObject);
}
object = new Object(a, b, innerList);
} else {
object = new Object(a, b);
}
}
}
@Override
public void onCancelled(@NonNull DatabaseError error) {
Log.d(TAG, "onCancelled error is : " + error.getMessage());
pd.dismiss();
}
});
Enjoy !!
Kotlin Example
This is a example to get all your nested data out of firebase real time database.
There are many tokens in this example and every token contains list of notes
val noteListener = object : ValueEventListener {
override fun onDataChange(dataSnapshot: DataSnapshot) {
// For each loop will run until all notes are fetched
for (snapshot in dataSnapshot.children){
val note = snapshot.getValue(Note::class.java)
Log.e("Note",note?.title)
}
}
override fun onCancelled(databaseError: DatabaseError) {
// On failed, log a message
Log.e("Debug", "loadPost:onCancelled", databaseError.toException())
}
}
//You can use the addListenerForSingleValueEvent() method to simplify this scenario: it triggers once and then does not trigger again.
//ref.child(token) is the path for reach notes for each unique user , in your case it can be ref only
ref.child(token).addListenerForSingleValueEvent(noteListener)
I'm not sure if this is the case, but may help others: Do not store null
's on List
's
List
's as Map
's with index as keys, when deserialized back it detects the correlative keys and it treats it as a 'List'So a List
with a null value won't have all indexes and, when parsed, Firebase thinks that it's a Map
.
Store some kind of "empty" value instead of null
and it'll work.
Side note: if you have a map
with correlative indexes it'll happen a similar error, just add some not numeric prefix on keys.