Я пытаюсь десериализовать простой документ Firebase Firestore для приложения Android.
Проблема связана с десериализацией вложенных классов Kotlin. И это должно быть довольно просто, но почему-то кажется, что он не работает.
У меня есть класс, который хотел бы хранить в качестве класса данных Kotlin содержимое карты на удаленный документ.
Даже несмотря на то, что имена полей, предоставленные @PropertyName, совпадают с именами свойств связанного класса и невложенного класса (называемого Bucket) правильно принимает все «примитивные» данные, его Вложенные классы просто не заполняются удаленными данными, возвращаясь к значениям пустого конструктора, которые вы должны предоставить, создавая таким образом мусорный объект.
Детали реализации
Структура данных документа Firebase Bucket следующая:

Я создал следующий класс для представления всего документа:
Код: Выделить всё
@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())
}
Код: Выделить всё
@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())
}
Код: Выделить всё
28.4.2Код: Выделить всё
buckets.addSnapshotListener { data, error ->
val update: List = data.toObjects(Bucket::class.java)
}

Я не получаю ошибок, только предупреждение 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
Эти журналы озадачили меня, потому что я действительно создал методы получения и установки, поскольку Bucket — это класс данных. Или нет?
Я декомпилировал файл kotlin в Java и понял, что для нашего объекта не существует метода получения с именем getbucket_style():
Код: Выделить всё
@PropertyName("bucket_style")
@NotNull
private final BucketStyle style;
@NotNull
public final BucketStyle getStyle() {
return this.style;
}
Мы можем попытаться проделать трюк, переименовав свойство kotlin style в Bucket_style (чтобы сопоставить имя поля документа Firestore) таким образом в Bucket:
Код: Выделить всё
@PropertyName("bucket_style")
val bucket_style: BucketStyle,
Код: Выделить всё
@PropertyName("bucket_style")
@NotNull
private final BucketStyle bucket_style;
@NotNull
public final BucketStyle getBucket_style() {
return this.bucket_style;
}
Однако, если я создам собственный геттер с именем getbucket_style() в исходном сегменте, например:
Код: Выделить всё
@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())
}
Код: Выделить всё
@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!)
Если я проделаю тот же трюк, что и раньше, переименование 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())
}
Код: Выделить всё
@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 для моделирования вложенных карт Firestore, но работать с классами гораздо приятнее. с.
Я также пытался разблокировать все классы, но проблема не устранена.
Есть ли чистое решение / что-то не так с реализация?
ИЗМЕНИТЬ
Обходной путь — изменить имена свойств класса, чтобы они соответствовали именам полей документа Firestore.
Моя проблема — это частный случай более общей ошибки. Неправда, что десериализация не работает для подклассов. Это не работает для любого поля класса, имя которого не соответствует Firebase (в моем случае).
Действительно, все, что мне нужно было сделать, это изменить имя карты с «bucket_style» на «style», а внутренние поля (теперь на карте «style») использовать верблюжий регистр (чтобы соответствовать объявлениям моего приложения) и вуаля, данные теперь идут туда, куда и должны. Теперь я использую одни и те же имена на бэкенде и в своей модели, чтобы десериализатор не запутался.
Тем не менее, я не знаю, как это не работает . Я считаю, что это то, что я запускаю (я отказываюсь верить, что до сих пор никто не сталкивался с этой проблемой и не сообщал о ней), хотя мое приложение все еще маленькое, следуйте рекомендациям де-факто и используйте Firebase исключительно для пожарный магазин.
Подробнее здесь: https://stackoverflow.com/questions/697 ... on-android
Мобильная версия