【发布时间】:2021-12-13 05:08:44
【问题描述】:
问题描述
我正在尝试反序列化 Android 应用的简单 Firebase Firestore 文档。
问题在于 kotlin 嵌套类反序列化。它应该是一个非常简单的方法,但它似乎不起作用。
我有一个类希望将远程文档上的地图内容存储为 kotlin 数据类。
尽管@PropertyName-提供的字段名称与关联的类属性名称匹配,并且非嵌套类(称为Bucket)正确接收所有“原始”数据,但它的嵌套类似乎没有被填充使用远程数据,回退到您必须提供的空构造函数值,从而创建一个垃圾对象。
实现细节
FirebaseBucket文档数据结构如下:
我创建了以下类来表示整个文档:
@Parcelize
data class Bucket(
@PropertyName("id")
val id: String,
@PropertyName("name")
val name: String,
@PropertyName("description")
val description: String,
@PropertyName("bucket_style")
val style: BucketStyle,
@PropertyName("bucket_dates")
val dates: BucketDates
) : Parcelable {
constructor() : this("mock.id", "mock.name", "mock.desc", BucketStyle(), BucketDates(), listOf())
}
以BucketStyle 类为例:
@Parcelize
data class BucketStyle(
@PropertyName("icon_family")
val iconFamily: Int,
@PropertyName("icon_id")
val iconId: Int,
@PropertyName("color_id")
val colorId: Int
) : Parcelable {
constructor() : this(0, 0, 1)
}
并且对称:
@Parcelize
data class BucketDates(
@PropertyName("creation_date_timestamp")
val creationDate: Timestamp,
@PropertyName("last_edit_timestamp")
val lastEdit: Timestamp
) : Parcelable {
constructor() : this(Timestamp.now(), Timestamp.now())
}
我使用默认的 firebase 提供的方法(28.4.2 的 firebase-bom 版本)反序列化它(实际上是使用实时侦听器在集合中的所有文档)
buckets.addSnapshotListener { data, error ->
val update: List<Bucket> = data.toObjects(Bucket::class.java)
}
并且没有填充内部类,如此调试器所示,因为服务器值不同,它们使用默认值,而 ID、名称和其他所有“原始”都可以正常工作。
我没有收到错误,只是在执行时出现 logcat 警告:
W/Firestore: (23.0.4) [CustomClassMapper]: No setter/field for bucket_style found on class com.[redacted].model.bucket.Bucket
W/Firestore: (23.0.4) [CustomClassMapper]: No setter/field for bucket_dates found on class com.[redacted].model.bucket.Bucket
补充分析
那些日志让我感到困惑,因为我确实创建了 getter 和 setter,因为 Bucket 是一个数据类。还是我?
我将 kotlin 文件反编译为 java,我意识到我们的对象没有名为 getbucket_style() 的 getter:
@PropertyName("bucket_style")
@NotNull
private final BucketStyle style;
@NotNull
public final BucketStyle getStyle() {
return this.style;
}
我们只有getStyle()。
我们可以尝试通过在Bucket 中将 kotlin style 属性重命名为 bucket_style(以匹配 firestore 文档字段名称)来做一个技巧:
@PropertyName("bucket_style")
val bucket_style: BucketStyle,
java反编译成:
@PropertyName("bucket_style")
@NotNull
private final BucketStyle bucket_style;
@NotNull
public final BucketStyle getBucket_style() {
return this.bucket_style;
}
但警告保持不变。生成的 getter 有一个大写的“B”(这是在 kotlin 数据类中生成 getter 的语法约定)。
但是,如果我在原始 Bucket 中创建一个名为 getbucket_style() 的自定义 getter,如下所示:
@Parcelize
data class Bucket(
@PropertyName("id")
val id: String,
@PropertyName("name")
val name: String,
@PropertyName("description")
val description: String,
@PropertyName("bucket_style")
val style: BucketStyle,
@PropertyName("bucket_dates")
val dates: BucketDates
) : Parcelable {
fun getbucket_style() = style
constructor() : this("mocker.id", "mocker.name", "mocker.desc", BucketStyle(), BucketDates(), listOf())
}
谁的java反编译长这样:
@PropertyName("bucket_style")
@NotNull
private final BucketStyle style;
@NotNull
public final BucketStyle getbucket_style() {
return this.style;
}
@NotNull
public final BucketStyle getStyle() {
return this.style;
}
然后警告改变了!
W/Firestore: (23.0.4) [CustomClassMapper]: No setter/field for style found on class com.[redacted].model.bucket.Bucket (fields/setters are case sensitive!)
我不知道为什么。另请注意,在第一个警告中,该属性称为bucket_style,此处称为style。
如果我像以前一样做同样的把戏,将 style 重命名为 bucket_style,我们会得到:
@Parcelize
data class Bucket(
@PropertyName("id")
val id: String,
@PropertyName("name")
val name: String,
@PropertyName("description")
val description: String,
@PropertyName("bucket_style")
val bucket_style: BucketStyle,
@PropertyName("bucket_dates")
val dates: BucketDates
) : Parcelable {
fun getbucket_style() = bucket_style
constructor() : this("mock.id", "mock.name", "mock.desc", BucketStyle(), BucketDates(), listOf())
}
java反编译的样子
@NotNull
private final String description;
@PropertyName("bucket_style")
@NotNull
public final BucketStyle getbucket_style() {
return this.bucket_style;
}
@NotNull
public final BucketStyle getBucket_style() {
return this.bucket_style;
}
这会在没有警告的情况下崩溃,因为(我相信)有两个函数具有相同的、不区分大小写的名称:
E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.[redacted], PID: 18996
java.lang.RuntimeException: Found conflicting getters for name getbucket_style on class com.[redacted].model.bucket.Bucket
结论
另一种方法是使用 kotlin Maps 对嵌套的 firestore 地图进行建模,但类更易于使用。
我也尝试取消包裹所有课程,但问题仍然存在。
是否有干净的解决方案/实施有问题?
编辑
解决方法是更改类属性名称以匹配 Firestore 文档字段名称。
我的问题是一个更普遍的错误的特例。反序列化对子类不起作用是不正确的。它不适用于名称与 firebase 不匹配的 任何 类字段(在我的情况下)。
确实,我所要做的就是将地图名称从“bucket_style”更改为“style”,并将内部字段(在现在的“style”地图中)更改为使用驼峰式大小写(以匹配我的应用程序声明)和瞧,数据现在去往它应该去的地方。现在我在后端和模型上使用相同的名称,这样反序列化器就不会混淆。
不过,我不知道这怎么行。我相信这是我正在触发的事情(我拒绝相信到目前为止没有人遇到此问题并报告它),即使我的应用程序仍然很小,请遵循 de facto 准则并专门使用 firebase火库。
【问题讨论】:
-
您尝试过使用地图吗?是这样的吗?
-
嘿!我会试试的,我也可以尝试用另一个库解码 JSON(我在想 gson)。这些可能会起作用,但是恕我直言,它们不是很干净的解决方案,我想了解是否(以及如何)使用映射到类的反序列化功能。
-
这也应该适用于类,但您所要做的就是在反序列化时注意您使用的名称。
-
就是这样,我认为我做的一切都是正确的,但它似乎并没有按预期工作。
标签: android firebase kotlin serialization google-cloud-firestore