Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

"RuntimeException: Could not deserialize object" while reading nested object from Firestore

JSON structure:

{
    "breviario": {
        "metaLiturgia": {
                "fecha"  : "Martes  5 de febrero del 2019",
                "tiempo" : "PROPIO DE LOS SANTOS",
                "semana"   : "",
                "mensaje": "",
                "salterio": "",
                "color":0,
                "meta": ""
        },
        "santo": {
                "nombre": "Santa Águeda, virgen y mártir",
                "vida": "Padeció el martirio en Catania (Sicilia), probablemente en la persecución de Decio. Desde la antigüedad su culto se extendió por toda la Iglesia y su nombre fue introducido en el Canon romano."
        },

        "oficio": {
            "himno": {
                "texto": "Testigos de amor~de Cristo Señor,~mártires santos.§Rosales en flor~de Cristo el olor,~mártires santos.§Palabras en luz~de Cristo Jesús,~mártires santos.§Corona inmortal~del Cristo total,~mártires santos. Amén."
            },
            "salmodia": {
  ... 


Oficio:

Structure of Ofcio

public class Oficio {
    private Invitatorio invitatorio;
    private Himno himno;
    private Salmodia salmodia;
    private String oracion;
    private String responsorio;
    private OficioLecturas oficioLecturas;
    public Oficio () {}

    public Himno getHimno() {
        return himno;
    }

    public void setHimno(Himno himno) {
        this.himno = himno;
    }
// ...
}


Himno:

Structure of Himno

public class Himno {
    private String texto;
    public Himno () {}

    public Spanned getTexto() {
        Spanned str = Utils.fromHtml(Utils.getFormato(texto));
        return str;
    }

    public void setTexto(String texto) {
        this.texto = texto;
    }
    //...
}


What I have tried:

    DocumentReference docRef = db.collection("liturgia").document("breviario")
            .collection("oficio").document("20190204");
    docRef.get().addOnSuccessListener(new OnSuccessListener<DocumentSnapshot>() {
        @Override
        public void onSuccess(DocumentSnapshot documentSnapshot) {
            Oficio oficio = documentSnapshot.toObject(Oficio.class);
            Himno himno=oficio.getHimno();
            Log.d(TAG,oficio.getOracion().toString()); //Correct
        }
    });


The problem:

I can't read the property himno as the custom class 'Himno'. When I try, I get a 'RuntimeException' even if I comment the line: Himno himno=oficio.getHimno();. I can however get the property oracion into the corresponding variable.


Stack trace:

E/AndroidRuntime: FATAL EXCEPTION: main Process: org.my.app, PID: 10532 java.lang.RuntimeException: Could not deserialize object. Can't convert object of type com.google.firebase.firestore.DocumentReference to type org.my.app.model.Himno (found in field 'himno') at com.google.firebase.firestore.util.CustomClassMapper.deserializeError(com.google.firebase:firebase-firestore@@17.1.2:524) at com.google.firebase.firestore.util.CustomClassMapper.convertBean(com.google.firebase:firebase-firestore@@17.1.2:505) at com.google.firebase.firestore.util.CustomClassMapper.deserializeToClass(com.google.firebase:firebase-firestore@@17.1.2:242) at com.google.firebase.firestore.util.CustomClassMapper.deserializeToType(com.google.firebase:firebase-firestore@@17.1.2:180) at com.google.firebase.firestore.util.CustomClassMapper.access$200(com.google.firebase:firebase-firestore@@17.1.2:53) at com.google.firebase.firestore.util.CustomClassMapper$BeanMapper.deserialize(com.google.firebase:firebase-firestore@@17.1.2:700) at com.google.firebase.firestore.util.CustomClassMapper$BeanMapper.deserialize(com.google.firebase:firebase-firestore@@17.1.2:674) at com.google.firebase.firestore.util.CustomClassMapper.convertBean(com.google.firebase:firebase-firestore@@17.1.2:503) at com.google.firebase.firestore.util.CustomClassMapper.deserializeToClass(com.google.firebase:firebase-firestore@@17.1.2:242) at com.google.firebase.firestore.util.CustomClassMapper.convertToCustomClass(com.google.firebase:firebase-firestore@@17.1.2:97) at com.google.firebase.firestore.DocumentSnapshot.toObject(com.google.firebase:firebase-firestore@@17.1.2:203) at com.google.firebase.firestore.DocumentSnapshot.toObject(com.google.firebase:firebase-firestore@@17.1.2:183)

like image 537
A. Cedano Avatar asked Feb 05 '19 12:02

A. Cedano


2 Answers

You are getting the following error:

Can't convert object of type com.google.firebase.firestore.DocumentReference to type org.my.app.model.Himno (found in field 'himno')

Because you are trying to convert a DocumentReference object to a Himno object. There is no way in Java to achieve this since there is no inheritance relationship between them.

The document that you are trying to get from the following location:

db.collection("liturgia").document("breviario").collection("oficio").document("20190204");

Is of type Oficio, so the following line of code:

Oficio oficio = documentSnapshot.toObject(Oficio.class);

Will work perfectly fine. The problem is when you are trying to get the Himno object which is nested under your Oficio class like this:

Himno himno=oficio.getHimno();

And this will not work in the way you do since your himno property inside the document holds a value which is of type DocumentReference and not of type Himno.

enter image description here

See, the himno property has a reference. If you want to get that document reference, simply use:

DocumentReference documentReference = documentSnapshot.getDocumentReference("himno");

If you want to use:

Himno himno=oficio.getHimno();

Then change the himno property to be of type Himno and not DocumentReference.

Another issue in your code, as also @Anees pointed out in his aswer is that the getTexto() method should return a String and not a Spanned object because this the way in which is stored in the database.

enter image description here

See, the texto property holds a String and not a Spanned. But this is not the reason why your error occurs.

Please also note, that the following sentence:

Custom classes in Firestore must contain

Is incorrect! There isn't a must for the public no-argument constructor nor for the public getters.

So you class might simply look like this:

public class Oficio {
    public Invitatorio invitatorio;
    public Himno himno;
    public Salmodia salmodia;
    public String oracion;
    public String responsorio;
    public OficioLecturas oficioLecturas;
}

Without any constructors, setters or getters at all. More informations here.

Edit:

According to your comment:

I changet the type of getTexto() from Spanned to String, but is not working.

Changing only the return type of your getTexto() method from Spanned to String will not help you solve the main error.

I don't understand how i can change the himno property to be of type Himno and not DocumentReference.

You haven't shared the way you are adding the data to the database but it's actually very simple. First, remove that himno property from those documents. Then, when you are adding a new document, instead of adding a DocumentReference, add an object of type Himno, as I also see that you have declared in your Oficio class. This also means, that when you'll use the following line of code:

Himno himno=oficio.getHimno();

An object of type Himno will be returned and not a DocumentReference. This is because it is stored accordingly to its data type.

In RealTimeDatabase is possible to take only one object (Oficio par example) and from this object i can get references to another nested object (Himno par example).

This can be also done in Cloud Firestore but you need to get the data according to the data type of your property it exists in the database. If your property is of type DocumentReference, you cannot get it as a type Himno, unless you add it like so.

See the section named "Custom objects" in the docs

Yes, I see. That's why I told you to get the data according to the data type it is stored. So if you have the data stored as a DocumentReference, get it according to it. If you want to get it as a Himno, store it as a Himno in the first place and then get it accordingly.

Edit2:

Are you telling me that in can create a field of type Himno in Firestore?

Yes, you can do it. How did you add that reference in the database? In the same way you added that reference add the Himno object.

I don't understand how i can achieve this.

When you are adding data to the database by creating an object of type Oficio, set the Himno object in correct a way. Try this:

Oficio oficio = new Oficio();
Himno himno = new Himno();
himno.setTexto("Your Text");
oficio.setHimno(himno);
docRef.set(oficio);

I'm misunderstanding things about it?

Yes. To able to manage a Cloud Firestore database, you should have at least basic understanding on:

  • How to create objects in Java
  • How set object properties
  • How to add data to Cloud Firestore
  • What are collections / documents
  • Allowed data type in Firestore
like image 194
Alex Mamo Avatar answered Sep 21 '22 17:09

Alex Mamo


Custom classes in Firestore must contain:

  • A public default constructor (A constructor which takes no argument)
  • Public getters (of the same type) for each property


Here is what you are missing

texto from the class Himno does not have a public getter (of the same type). Your getter returns a Spanned.


Add this to the class Himno:

public String getTexto() {
    return this.textTo;
}
like image 29
Bertram Gilfoyle Avatar answered Sep 21 '22 17:09

Bertram Gilfoyle