将 Firebase 数据快照反序列化为 Kotlin 数据类

Posted

技术标签:

【中文标题】将 Firebase 数据快照反序列化为 Kotlin 数据类【英文标题】:Deserialize a Firebase Data-Snapshot to a Kotlin data class 【发布时间】:2018-11-16 07:06:08 【问题描述】:

您好,我有一个 Kotlin 数据类,如下所示

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
)

我能够毫无问题地序列化对象。但是我在进行 firebase 查询时无法反序列化对象。目前这就是我正在做的获取数据

_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())
                
            
        

    )

这只是我正在做的一个测试项目,用于练习我的 Kotlin,但这是我想弄清楚的。

如果我做错了,请告诉我,任何建议将不胜感激

谢谢

【问题讨论】:

你得到什么错误? 对不起,上面的代码可以工作,但它只是混乱,如果我尝试使用 getValue(User::class.java) 反序列化我得到错误:用户没有定义无参数构造函数如果我添加一个第二个构造函数然后我无法使用值的键,因为我无法注释第二个构造函数 为什么说是乱七八糟的?不是。 我习惯了 C# 并使用 Newtonsoft 库,它允许我为我的数据类注释我的属性,我希望我能找到类似的解决方案 【参考方案1】:

Firebase 需要一个空的构造函数才能反序列化对象:

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, "", "", "", "", "")

您可以像这样声明它并提供一些默认值以便能够调用主构造函数,或者您可以为所有参数声明默认值:

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 = ""
)

然后会为你创建各种构造函数,包括一个空的构造函数。

如果序列化出现问题,可能是由于 ide 生成的 getter 和 setter,请尝试使用 @get 和 @set 注释加强它们:

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 = ""
)

【讨论】:

您好,感谢您回复我这个解决方案的唯一问题是当我调用 p0.getValue(User::class.java) 时,显示名称和帐号图片 url 的 json 不会反序列化进入对象,因为数据库上的名称与属性的名称不匹配,知道如何解决这个问题而不必重命名数据库节点? 谢谢我忘记了 var 和 val 所以这就是为什么我在完美地工作时遇到了麻烦谢谢!! 嗨,@LeviMoreira,我可以就我的问题向你寻求帮助吗,这里是:***.com/questions/54454616/…【参考方案2】:

我真正想要的是一个 Kotlin 数据类,它派生自这样的领域模型接口

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

在这种情况下,DomainModel 是这样定义的

interface DomainModel  val title: String 

我的目标是从 Firestore 获取数据并获取反序列化的 Dto 对象,这些对象提供给接收 DomainModel 类型对象的客户端。所以不幸的是,上面的这个解决方案没有奏效。我看到了使用 @get: 和 @set: Annotations 的解决方法,但我希望我的数据类属性是不可变的。在我的用例中,简单地使用 vars 是一个糟糕的设计决策。而且这个解决方案看起来很丑......

在检查了反编译的 Java 代码后,我想出了这个解决方案

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

反编译的 Java 代码仅使用标题作为具有 PropertyName 注释的公共最终字段。

我更喜欢这个解决方案,因为它不会违反我做出的某些设计决策...

【讨论】:

【参考方案3】:

android Studio (kotlin) 中 使用这个(只有 var 和 getter 和 setter):

@set:PropertyName("email") @get:PropertyName("email") var emailPerson: String = ""

这些都不起作用:

@PropertyName("email") var emailPerson: String = "" @PropertyName("email") val emailPerson: String = "" @get:PropertyName("email") val emailPerson: String = ""

Android Studio 4.1.2。 Gradle:com.google.firebase:firebase-database:19.6.0

【讨论】:

以上是关于将 Firebase 数据快照反序列化为 Kotlin 数据类的主要内容,如果未能解决你的问题,请参考以下文章

如何将地理位置点数据反序列化为 Java 实体?

如何将具有固定模式的值数组反序列化为强类型数据类?

使用 JsonConvert 将 JSON 反序列化为 VB.NET 中的数据表

如何使用 kotlinx 序列化将值数组反序列化为集合

不使用 XmlDocument.Loadxml() 函数将 XML 反序列化为 JSON

将类反序列化为文件并返回列表