Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Deserialize a Firebase Data-Snapshot to a Kotlin data class

Hi I have a Kotlin data class as follows

data class User (
        @get:Exclude val gUser: Boolean,
        @get:Exclude val uid: String,
        @get:PropertyName("display_name") val displayName: String,
        @get:PropertyName("email") val email: String,
        @get:PropertyName("account_picture_url") val accountPicUrl: String,
        @get:PropertyName("provider") val provider: String
)

I am able to serialize the object without an issues. But i'm having trouble deserializing the object when doing a firebase query. Currently this is what i'm doing to get the data

_firebaseReference.child(getString(R.string.firebase_users_key)).child(user.uid)
        .setValue(user).addOnCompleteListener{
    _firebaseReference.child("users").child(user.uid)
            .addListenerForSingleValueEvent(object : ValueEventListener {
        override fun onCancelled(p0: DatabaseError) {

        }

        override fun onDataChange(p0: DataSnapshot) {
            if (p0.exists()) {
                val userHash = p0.value as HashMap<*, *>
                var currentUser: User
                if (userHash[getString(R.string.provider_key)]
                        != getString(R.string.provider_google)) {
                    currentUser = User(false, p0.key!!, 
                            userHash["display_name"].toString(), 
                            userHash["email"].toString(),
                            userHash["account_picture_url"].toString(), 
                            userHash["provider"].toString())
                } else {
                    currentUser = User(true, p0.key!!, 
                            userHash["display_name"].toString(), 
                            userHash["email"].toString(), 
                            userHash["account_picture_url"].toString(), 
                            userHash["provider"].toString())
                }
            }
        }

    })
}

This is only a test project that i'm working on to practice my Kotlin, but this is something I would like to figure out.

If i'm doing it completely wrong please let me know, any advise would be greatly appreciated

Thanks

like image 646
aidan8181 Avatar asked Jun 06 '18 14:06

aidan8181


People also ask

How do I get DataSnapshot data?

A DataSnapshot is passed to the event callbacks you attach with on() or once() . You can extract the contents of the snapshot as a JavaScript object by calling the val() method. Alternatively, you can traverse into the snapshot by calling child() to return child snapshots (which you could then call val() on).

What does getKey () do firebase?

Calling the getKey() method on this reference will return the auto-generated key which may then be used to store a corresponding value. Using Firebase to generate unique keys in this way is also of particular use when an app has multiple users creating child nodes at the same path in the tree.

What is data snapshot firebase?

A DataSnapshot is an efficiently-generated immutable copy of the data at a Firebase Location.

Can we retrieve data from Firebase?

Firebase data is retrieved by either a one time call to GetValueAsync() or attaching to an event on a FirebaseDatabase reference. The event listener is called once for the initial state of the data and again anytime the data changes.


2 Answers

Firebase needs an empty constructor to be able to deserialize the objects:

data class User(
        @Exclude val gUser: Boolean,
        @Exclude val uid: String,
        @PropertyName("display_name") val displayName: String,
        @PropertyName("email") val email: String,
        @PropertyName("account_picture_url") val accountPicUrl: String,
        @PropertyName("provider") val provider: String
) {
    constructor() : this(false, "", "", "", "", "")
}

You can either declare it like so and provide some default values to be able to call the primary constructor or you can declare default values for all your parameters:

data class User (
        @Exclude val gUser: Boolean = false,
        @Exclude val uid: String = "",
        @PropertyName("display_name") val displayName: String = "",
        @PropertyName("email") val email: String = "",
        @PropertyName("account_picture_url") val accountPicUrl: String = "",
        @PropertyName("provider") val provider: String = ""
)

Then various constructors will be created for you, including an empty constructor.

If there's a problem with serialization there might be because of the getters and setters generated by the ide, try reinforcing them with @get and @set annotations:

data class User (
        @Exclude val gUser: Boolean = false,
        @Exclude val uid: String = "",
        @set:PropertyName("display_name") 
        @get:PropertyName("display_name")
        var displayName: String = "",
        @PropertyName("email") val email: String = "",
        @set:PropertyName("account_picture_url")
        @get:PropertyName("account_picture_url")
        var accountPicUrl: String = "",
        @PropertyName("provider") val provider: String = ""
)
like image 125
Levi Moreira Avatar answered Oct 16 '22 09:10

Levi Moreira


What I actually wanted is a Kotlin data class which is derived from a domain model interface like so

data class Dto(@PropertyName("serialized_title") val override title: String) : DomainModel

In this case DomainModel is defined this way

interface DomainModel { val title: String }

My goal was to fetch data from Firestore and get deserialized Dto objects which are provided to clients which receive objects of type DomainModel. So this solution above unfortunately didn't work. I saw the workarounds using @get: and @set: Annotations but I wanted my data class properties to be immutable. Simply using vars is a bad design decision in my use case. And also this solution looks quite ugly...

After inspecting the decompiled Java-Code I came up with this solution

data class Dto(
@field:[JvmField PropertyName("serialized_title")]
override val title: String = "") : DomainModel

The decompiled Java-Code simply uses title as public final field having the PropertyName annotation.

I prefer this solution since it doesn't violate certain design decisions I made...

like image 40
postfixNotation Avatar answered Oct 16 '22 09:10

postfixNotation