Firebase "Map while deserializing, but got a class java.util.ArrayList"

Aufa Husen picture Aufa Husen · Jul 7, 2016 · Viewed 8.7k times · Source

I am just trying out Firebase on android for my project.

the problem I got is that every time I take a snapshot and "cast" it back to POJO I get this -

"Map while deserializing, but got a class java.util.ArrayList" Exception.

I've been looking around and even change all my models imlementation using HashMap without any ArrayList at all and still got the same result.

Here's are my models:

  public class DevicesController {

        public DevicesCollection devices;
        public int numberOfport;
        String timerStatus;

        public DevicesController(){

            devices = new DevicesCollection();
            timerStatus = "UPDATED";
        }

        public DevicesController(int numberOfport){
            devices = new DevicesCollection();
            this.numberOfport = numberOfport;
            timerStatus = "UPDATED";
        }



        public ArrayList<Integer> getCurrentState(String in){
            ArrayList<Integer> currentState = new ArrayList<Integer>();
            for(int i = 0; i < numberOfport; i++){
                currentState.add(0);
            }
            Iterator it = this.getDevices().getAllDevices().entrySet().iterator();
            while (it.hasNext()) {
                Map.Entry pair = (Map.Entry) it.next();
                Device temp =(Device)pair.getValue();
                if(temp.isOn()){
                    currentState.set(temp.getPort(),1);
                }else if(!temp.isOn()){
                    currentState.set(temp.getPort(),0);
                }
            }

            return currentState;
        }


        public String getStringCurrentState(String in){
            ArrayList<Integer> currentState = getCurrentState("");
            String temp ="";
            for(int i = 0; i < currentState.size();i++){
                temp.concat(""+currentState.get(i));
            }
            return temp;
        }

        public void updateToDb(){
            Global.dbMRoot.child("devicesController").setValue(this);
        }

        public DevicesCollection getDevices() {
            return devices;
        }

        public int getNumberOfport() {
            return numberOfport;
        }

    }

    public class Category {
        public String name;
        public HashMap<String,String> devicesId;
        public DeviceAlarm categoryAlarm;

        public Category(){

        }

        public Category(String name) {
            devicesId = new HashMap<String,String>();
            this.name = name;
        }

        public void addDevice(Device dev){
            devicesId.put(dev.getId(),dev.getId());
        }


        public void removeDevice(Device dev){
            devicesId.remove(dev.getId());
        }

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }

        public HashMap<String,String> getDevicesId() {
            return devicesId;
        }

        public void setDevicesId(HashMap<String,String> devicesId) {
            this.devicesId = devicesId;
        }

    }

public class Device {

    public String id;
    public int port;
    public String name;
    public boolean on;
    public  DeviceAlarm alarm;

    public Device(){

    }


    public Device(String id,String name){
        this.id = id;
        this.name = name;
    }

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
        updateToDb();
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        updateToDb();
        this.name = name;
    }

    public boolean isOn() {
        return on;
    }

    public void setOn(boolean on) {
        updateToDb();
        this.on = on;
    }

    public void updateToDb(){
        Global.dbMRoot.child("devicesController").child("devices").child("allDevice").child(""+id).setValue(this);
    }
    public DeviceAlarm getTimer() {

        return alarm;
    }


    public int getPort() {

        return port;
    }

    public void setPort(int port) {
        updateToDb();
        this.port = port;
    }

    public DeviceAlarm getAlarm() {
        return alarm;
    }

    public void setAlarm(DeviceAlarm alarm) {
        Global.dbMRoot.child("devicesController").child("timerStatus").setValue("CHANGED");
        this.alarm = alarm;
        updateToDb();
    }

    public void setAlarm(boolean active, Calendar timeOn, Calendar timeOff) {
        Global.dbMRoot.child("devicesController").child("timerStatus").setValue("CHANGED");
        DeviceAlarm temp = new DeviceAlarm();
        temp.setTimeOff(timeOff);
        temp.setTimeOn(timeOn);
        this.alarm = temp;
        updateToDb();
    }
}


package com.lucerna.afgadev.lucerna_maincontroller.Models;

import com.lucerna.afgadev.lucerna_maincontroller.Global;

import java.util.ArrayList;
import java.util.HashMap;

/**
 * Created by aufa on 06/07/2016.
 */
public class DevicesCollection {

    public HashMap<String,Device> allDevices;
    public HashMap<String,Category>  categories;
    int lastId;

    public DevicesCollection(){
        categories = new HashMap<String,Category>();
        lastId = 0;
        allDevices = new HashMap<String, Device>();

    }

    public HashMap<String,Category> getCategories() {
        return categories;
    }

    public HashMap<String,Device> getAllDevices() {
        return allDevices;
    }

    public void addCategory(String name){
        categories.put(name,new Category(name));
    }

    public void removeCategory(String name) throws Exception{
        int index = -1;
        for(int i = 0; i< categories.size(); i++){
            if(categories.get(i).getName().equals(name)){
                index = 1;
            }
        }

        if(index == -1){
            throw new Exception("Category not found");
        }else{
            categories.remove(index);
        }

        updateToDB();

    }

    public void updateToDB(){
        Global.dbMRoot.child("devicesController").child("devices").setValue(this);
    }

    public Category findCategory(String name){
        Category temp = null;
        for(int i = 0; i< this.getCategories().size(); i++){
            if(this.getCategories().get(i).getName().equals(name)){
                temp = this.getCategories().get(i);
            }
        }
        return temp;
    }

    public void switchCategory(String name, boolean on){
        Category cat = findCategory(name);
        for(int i = 0; i < cat.getDevicesId().size();i++){

        }
    }


    public void addDevice(String name, int port){
        Device temp = new Device();
        temp.setPort(port);
        temp.setName(name);
        allDevices.put(""+lastId,temp);
        this.lastId++;
        Global.dbMRoot.child("devicesController").child("devices").child(""+lastId).setValue(allDevices.get(lastId));
    }

    public void removeDevice(int id){
        allDevices.remove(id);
        updateToDB();
    }

    public void setCategoryDevicesAlarm(DeviceAlarm da){

    }

}

the sample JSON Tree GDrive

I still haven't got any idea on why it happens, and i've re-checked to make sure that none of the is an ArrayList (i know firebase suppose to support arrayList though)

Thanks for all the comments and answers!

Answer

Bob Snyder picture Bob Snyder · Jul 8, 2016

I have a partial answer but Frank's expertise is required to explain what is really going on.

Running the posted code with version 9.2.0, I observe that the HashMap for allDevices is stored as a JSON array:

public class DevicesCollection {

    public HashMap<String,Device> allDevices; <-- this is stored as array
    public HashMap<String,Category>  categories;
    int lastId;

This is visible in the JSON posted by Aufa:

{
  "devicesController" : {
    "devices" : {
      "allDevices" : [ { <-- note bracket
        "name" : "",
        "on" : true,
        "port" : 0
      }, {

This appears to happen because the keys for the elements of allDevices are Strings in the form of an integer. This is the code that adds an entry to the map of allDevices:

public void addDevice(String name, int port){
    Device temp = new Device();
    temp.setPort(port);
    temp.setName(name);
    allDevices.put(""+lastId,temp); <-- Note that lastId is an integer
    this.lastId++;
    Global.dbMRoot.child("devicesController").child("devices").child(""+lastId).setValue(allDevices.get(lastId));
}

If this code is modified to use a String key that is not in integer format, for example:

allDevices.put("ID"+lastId,temp);

Then the map is written as a map and can be read using

getValue(DevicesCollection.class);