Android-Expected a List while deserializing, but got a class java.util.HashMap

Có Tạ Văn :

I want to get all list of restaurants from Firebase in android. my database

Here is my code:

boolean delivery;
String openTime, closeTime, restaurantName;
long likes;
List<String> utilities;

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);
                }
        }

        @Override
        public void onCancelled(@NonNull DatabaseError databaseError) {

        }
    };
    dataResReference.addListenerForSingleValueEvent(valueEventListener);
    return  listRes;
}

But i get an exception

Expected a List while deserializing, but got a class java.util.HashMap

at RestaurantModel restaurantModel = dataValues.getValue(RestaurantModel.class);

Update: Base on Alex Mamo answser, i changed my code is:

public void getAllRestaurant(final WhereInterface whereInterface){
    ValueEventListener valueEventListener = new ValueEventListener() {
        @Override
        public void onDataChange(@NonNull DataSnapshot dataSnapshot) {
            //go to node restaurant because datasnapshot is parent node
            DataSnapshot dataSnapshotRes = dataSnapshot.child("restaurants");
            //

            for (DataSnapshot valueRes : dataSnapshotRes.getChildren()){
                RestaurantModel restaurantModel = valueRes.getValue(RestaurantModel.class);
                whereInterface.getAllRestaurantModel(restaurantModel);

            }
        }

        @Override
        public void onCancelled(@NonNull DatabaseError databaseError) {

        }
    };
    databaseReferenceRoot.addListenerForSingleValueEvent(valueEventListener);
}

But i still got this exception (:.

Anyone can know why?

And are there solution?

Thank you so much!

Alex Mamo :

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);
}

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=80108&siteId=1