Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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!

like image 347
Có Tạ Văn Avatar asked Jul 17 '18 11:07

Có Tạ Văn


3 Answers

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);
}
like image 94
Alex Mamo Avatar answered Nov 06 '22 18:11

Alex Mamo


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

like image 37
Joseph Ali Avatar answered Nov 06 '22 18:11

Joseph Ali


I'm not sure if this is the case, but may help others: Do not store null's on List's

  1. Firebase stores List's as Map's with index as keys, when deserialized back it detects the correlative keys and it treats it as a 'List'
  2. Null values are not stored, nulls delete the field

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.

like image 1
pauminku Avatar answered Nov 06 '22 18:11

pauminku