11

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)

2 Answers 2

7
+50

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
Sign up to request clarification or add additional context in comments.

12 Comments

Thans for your response. As @Anees sugest, i changet the type of getTexto() from Spanned to String, but is not working. I don't understand how i can change the himno property to be of type Himno and not DocumentReference. 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). In this way i map only one object.
Alex, May be I was wrong. But Why do you think it is incorrect? See the section named "Custom objects" in the docs
@Anees That sentence from the docs is correct only if you are using private fields and this should be mentioned there. If you have public fields there is no need for a constructor, getters or setters. Please see the example in the last part of my answer. A class like that will work perfectly fine. You should also try that. Thanks for the chat :)
"This should be mentioned there" Yes you are right.
Alex i don`t know if i wrong, but if the problem is not solved, i think i can't check the question as accepted.
|
4

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;
}

5 Comments

Thanks. If you see the class, i have a public getter : public Spanned getTexto() { ... }
@A.Cedano But, it needs to be the same type as the property (String in this case). Yours is Spanned. You just have to add one with String type without removing the existing.
Anees checking the source code here i can see that if o is not instance of Map throws the same exception i'm having. Tried to put the reference into a Map but i can't map this reference to the class model Himno. I can see the object com.google.firebase.firestore.DocumentReference@8103823bbut not possible have data that is into.
Have you tried the @PropertyName() annotation with value "himno.texto" to texto?
Reading in Google Groups i can think that now is not possible to query into sub-collections. In more comments of this response we can conclude the same thing: we can't use sub-collections as Foreign key.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.