Карта Firebase при десериализации, но получила класс java.util.ArrayList

Я просто пробую Firebase на Android для своего проекта.

у меня проблема в том, что каждый раз, когда я делаю снимок и "отбрасываю" его обратно в POJO, я получаю следующее -

«Карта при десериализации, но получил класс java.util.ArrayList» Исключение.

Я смотрел вокруг и даже менял все свои модели, используя HashMap без всякого ArrayList, и все равно получил тот же результат.

Вот мои модели:

  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){

    }

}

образец дерева JSON GDrive

Я до сих пор не понимаю, почему это происходит, и я перепроверил, чтобы убедиться, что ни один из них не является ArrayList (хотя я знаю, что firebase предполагает поддержку arrayList)

Спасибо за все комментарии и ответы!


person Aufa Husen    schedule 07.07.2016    source источник
comment
Можете ли вы включить минимальный JSON (в виде текста) вместо ссылки на JSON в свое сообщение? Также будет полезно, если вы немного сократите код, так как его довольно много для синтаксического анализа. Лучше всего вы получите помощь, если предоставите минимальный код для воспроизведения проблемы.   -  person Frank van Puffelen    schedule 07.07.2016
comment
Прошу прощения за коды, которые немного длинны ... Я действительно думаю, что главное в этом то, что ни одно из свойств объекта не является arrayylist, поэтому это довольно сбивает с толку. Вскоре я обновлю вопрос простым деревом json, спасибо за ответы   -  person Aufa Husen    schedule 08.07.2016
comment
Если код длинный, сократите его до необходимого минимума, который все еще воспроизводит проблему. Если вас интересует только поведение arrayylist, воспроизведите проблему с помощью простого списка массивов. Чем меньше код (и связанный с ним JSON), на который нам нужно обратить внимание, тем легче вам помочь.   -  person Frank van Puffelen    schedule 08.07.2016
comment
@Frank: Пожалуйста, посмотри на мой ответ   -  person Bob Snyder    schedule 08.07.2016


Ответы (3)


У меня есть частичный ответ, но необходимы знания Фрэнка, чтобы объяснить, что на самом деле происходит.

Запустив опубликованный код с версией 9.2.0, я заметил, что HashMap для allDevices хранится как массив JSON:

public class DevicesCollection {

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

Это видно в JSON, опубликованном Aufa:

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

Это происходит потому, что ключи для элементов allDevices - это строки в форме целого числа. Это код, который добавляет запись на карту 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));
}

Если этот код изменен для использования строкового ключа, не имеющего целочисленного формата, например:

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

Тогда карта записывается как карта и может быть прочитана с помощью

getValue(DevicesCollection.class);
person Bob Snyder    schedule 08.07.2016
comment
Наличие последовательных числовых индексов в качестве имен свойств действительно может объяснить отображение. См. устаревшую документацию по обработке массивов < / а>. - person Frank van Puffelen; 08.07.2016

Если вы составляете карту следующим образом:

    basketRef.addValueEventListener(object : ValueEventListener {
        override fun onDataChange(dataSnapshot: DataSnapshot) {
            if(dataSnapshot.value==null || dataSnapshot.value!!.equals(""))
                Enums.userBasket=UserItem()
            else
                Enums.userBasket = dataSnapshot.getValue(UserItem::class.java)
        }

        override fun onCancelled(p0: DatabaseError) {
            // Failed to read value
            Log.d(TAG, "Error= "+p0.message)
        }

    })

А в устройствах firebase дочерние элементы ids, такие как (-1:"",-2:"")

Firebase Android SDK видит, что это ArrayList, а не объект карты.

Приведенный ниже код отлично подходит для меня. Я не могу использовать приведенный выше код.

Enums.userBasket=UserItem()
    basketRef.addValueEventListener(object : ValueEventListener {
        override fun onDataChange(dataSnapshot: DataSnapshot) {
            if(dataSnapshot.value==null || dataSnapshot.value!!.equals(""))

            else{
                if(dataSnapshot.child("books")!=null){
                    dataSnapshot.child("books").children.forEach {
                        Enums.userBasket!!.books[it.key.toString()] =it.value.toString()
                    }
                }
                if(dataSnapshot.child("cards")!=null){
                    dataSnapshot.child("cards").children.forEach {
                        Enums.userBasket!!.cards[it.key.toString()] =it.value.toString()
                    }
                }
                if(dataSnapshot.child("exams")!=null){
                    dataSnapshot.child("exams").children.forEach {
                        Enums.userBasket!!.exams[it.key.toString()] =it.value.toString()
                    }
                }
            }

        }

        override fun onCancelled(p0: DatabaseError) {
            // Failed to read value
            Log.d(TAG, "Error= "+p0.message)
        }

    })

Мой класс UserItem:

 @IgnoreExtraProperties
data class UserItem(
    var books:MutableMap<String,String> = HashMap(),//id,value
    var cards:MutableMap<String,String> = HashMap(),//id,value
    var exams:MutableMap<String,String> = HashMap()//id,value
)

Или вы можете посмотреть эту ссылку. Firebase заставляет использовать карту, а не массив

person harun karaca    schedule 25.05.2020

У меня такая же ошибка. Но позже я узнал, что сначала я использовал переменную tags для List в моем классе данных Note.kt и сохранил ее в документе firestore, но позже я изменил то же имя переменной tags на Map и попытался получить данные из firestore. Здесь, в firestore, у меня то же имя tags, что и у List и Map, поэтому я получаю ошибки при извлечении данных из firestore. Моя проблема решена, когда я удаляю коллекцию и снова добавляю данные.

Note.kt

 data class Note(
 //val tags:List<String>? = null, //first with tags variable 
 val tags:Map<String,Any>? = null, //second with same tags variable

)

person Apps Developer    schedule 08.05.2021