29

I am having trouble retrieving a List from the Firebase. I have no trouble storing it, but as soon as I try to cast dataSnapshot.getValue() to ArrayList my app crashes, giving an exception:

HashMap cannot be casted to ArrayList

But when I tried to cast it to a HashMap, it also crashes, giving exception:

ArrayList can't be casted to hashmap

Need help please! Here is the code that is creating the problem:

Fire.addValueEventListener(new ValueEventListener() {
    @Override
    public void onDataChange(DataSnapshot dataSnapshot) {
        List<TaskDes> td = (ArrayList<TaskDes>) dataSnapshot.getValue()
        notifyDataSetChanged();
    }

    @Override
    public void onCancelled(FirebaseError firebaseError) {

    }
});

firebase object screen shot

I want to retrieve all the data in the Firebase as one List. The class TaskDes contains three fields:

class TaskDes { // definition
    boolean done
    String taskDescription
    String taskTitle
}
Frank van Puffelen
  • 565,676
  • 79
  • 828
  • 807
Junaid Warsi
  • 291
  • 1
  • 3
  • 4
  • Unfortunately there is not enough code here for me to help you troubleshoot. On the positive side: the AndroidChat sample app from Firebase does all that you are looking for and little more. Have a look at it here: https://github.com/firebase/AndroidChat/tree/master/app/src/main/java/com/firebase/androidchat . I recently explained a bit about its internals on the Firebase Google Group: https://groups.google.com/d/msg/firebase-talk/OCoQPvpgl1U/Nli_OywG7LYJ – Frank van Puffelen Jun 09 '15 at 23:39
  • Thanks i will give it a try, another thing i want to ask if i store the hashmap on the fire base then when retrieving it from the firebase can i directly cast the datasnapshot.getValue() to hashmap ?? – Junaid Warsi Jun 10 '15 at 10:47
  • Yes, that is possible. But only if you model the `Task` class correctly. Have a look at the example. Your `Task` should look like https://github.com/firebase/AndroidChat/blob/master/app/src/main/java/com/firebase/androidchat/Chat.java – Frank van Puffelen Jun 11 '15 at 01:22
  • I think that this [resource](https://medium.com/@alex.mamo/how-to-map-an-array-of-objects-from-realtime-database-to-a-list-of-objects-53f27b33c8f3) will definitely help solve the issue. – Alex Mamo Jun 25 '22 at 11:48

5 Answers5

43

You need to create a GenericTypeIndicator object to pass as DataSnapshot.getValue() parameter.

Code:

GenericTypeIndicator<List<String>> t = new GenericTypeIndicator<List<String>>() {};

List<String> yourStringArray = dataSnapshot.getValue(t);
Narendra Baratam
  • 828
  • 1
  • 12
  • 26
Felipe Rocha
  • 531
  • 4
  • 6
11

Your Model

public class TaskDes {
    private boolean done;
    private String taskDescription;
    private String taskTitle;

    public TaskDes() {
    }

    public boolean isDone() {
        return done;
    }

    public void setDone(boolean done) {
        this.done = done;
    }

    public String getTaskDescription() {
        return taskDescription;
    }

    public void setTaskDescription(String taskDescription) {
        this.taskDescription = taskDescription;
    }

    public String getTaskTitle() {
        return taskTitle;
    }

    public void setTaskTitle(String taskTitle) {
        this.taskTitle = taskTitle;
    }

}

You need to create a GenericTypeIndicator object to pass as DataSnapshot.getValue() parameter.

In Activity

 private static final String TAG=MainActivity.class.getSimpleName();
 private FirebaseDatabase database;
 private DatabaseReference myRef=null;

 @Override
 protected void onCreate(Bundle savedInstanceState){
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    database=FirebaseDatabase.getInstance();
    myRef=database.getReference("ADD_YOUR_REFERECE");


    myRef.addValueEventListener(new ValueEventListener(){
       @Override
       public void onDataChange(DataSnapshot dataSnapshot){
          /* This method is called once with the initial value and again whenever data at this location is updated.*/
          long value=dataSnapshot.getChildrenCount();
          Log.d(TAG,"no of children: "+value);

          GenericTypeIndicator<List<TaskDes>> genericTypeIndicator =new GenericTypeIndicator<List<TaskDes>>(){};

          List<TaskDes> taskDesList=dataSnapshot.getValue(genericTypeIndicator);

          for(int i=0;i<taskDesList.size();i++){
             Toast.makeText(MainActivity.this,"TaskTitle = "+taskDesList.get(i).getTaskTitle(),Toast.LENGTH_LONG).show();
          }
       }

       @Override
       public void onCancelled(DatabaseError error){
          // Failed to read value
          Log.w(TAG,"Failed to read value.",error.toException());
       }
    });
 }
Anand Kumar
  • 1,439
  • 1
  • 15
  • 22
Muhammad Waleed
  • 2,517
  • 4
  • 27
  • 75
2

Make another item that contains a list for your item: This is your item:

class TaskDes { // definition
    boolean done
    String taskDescription
    String taskTitle
}

This is the list item

        class TaskDesList { // definition
              private  ArreyList<TaskDes> yourlist
            }

      public TaskDesList(){
         }

        public ArrayList<TaskDes> getYourlist() {
            return yourlist;
        }

and when calling an EventListener

ref.addValueEventListener(new ValueEventListener() {
            @Override
            public void onDataChange(DataSnapshot dataSnapshot) {
                yourlist.clear();
             taskDesList=dataSnapshot.getValue(TaskDesList.class);
                if (taskDesList!=null) {
                    yourlist= taskDesList.getYourlist();
                }
}

and now "yourlist" is a list that contains all of your "TaskDes" items

1

A bit late, but in case any one else needs this.

IF the list is inside another object.

The object

public class Question {

    public Date date;
    public String questionNumber;
    public String questionText;
    public QuestionType questionType;
    public String multipleSelection1;
    public String multipleSelection2;
    public String multipleSelection3;

    public Question() {
        // Default constructor required for calls to DataSnapshot.getValue(User.class)
    }
}

Then to get your array of question objects

GenericTypeIndicator<List<Question>> t = new GenericTypeIndicator<List<Question>>() {};
List<Question> questionList = dataSnapshot.getValue(t);
Ruan
  • 3,969
  • 10
  • 60
  • 87
0

Apparently, the GenericTypeIndicator doesn't work for all List objects particularly when the object contains none primitive types like maps. So, if it didn't work for your use case as it didn't for me, try this alternate solution:

 @Override
 public void onDataChange(DataSnapshot dataSnapshot) {

    List<TaskDes> tDlist = new ArrayList<>();

    for (DataSnapshot d: dataSnapshot.getChildren()){
        TaskDes tD = d.getValue(TaskDes.class);
        tDlist.add(tD);
    }

    notifyDataSetChanged();
 }

As mentioned in the previous answers make sure your class( like TaskDes in this case) has a public constructor which is empty so the getValue method can deserialize correctly to your java class.

Njuacha Hubert
  • 388
  • 3
  • 14