为什么我可以将枚举保存到 Firestore,但当我尝试检索它时应用程序会崩溃?

Why can I save an enum to Firestore, but it crashes the app when I try to retrieve the same?

我定义了以下内容:

enum class Venues (val nickname: String) {
    Venue1 ("Venue 1"),
    Venue2 ("Venue 2")
}

data class Tournament (var aString: String = "Title",
                       var aDate: Date = Date(),
                       var anInt: Int = 8,
                       var venue: Venues = Venues.Venue1,
                       var aBoolean: Boolean = true)

Tournament 写入 Firestore 的以下代码按预期工作:

fun createNewTournament(view: View) {
    val teamCount = (8..24).random()
    val venue: Venues = Venues.values().random()

    val newTournament = Tournament("String", Date(), teamCount, venue, false)

    tournamentsRef.add(newTournament)
        .addOnSuccessListener { docRef -> Log.d ("ADD", "DocSnapShot has id: ${docRef.id}") }
        .addOnFailureListener {      e -> Log.w ("ADD", "Error adding document", e)}
}

查看 Firebase 控制台,我看到一个完整的 Tournament 文档,其中 venue 字段定义为 String

但是,当我尝试读取相同内容时,我的应用程序在 toObjects 行崩溃:

val tournamentsRef = FirebaseFirestore.getInstance().collection("tournaments")
var tournaments = ArrayList<Tournament>()

tournamentsRef.addSnapshotListener { snapshot, error ->
    if (error != null) {
        return@addSnapshotListener
    }

    // CRASH: this line crashes if enum is included
    tournaments = snapshot!!.toObjects(Tournament::class.java) as ArrayList<Tournament>
}

如果我从 data class 中删除 venue 枚举,所有文档都被成功读取,但显然没有 venue 字段。

那么,如何将 enums 与 Firestore 一起使用,具体来说,我如何将传入的 String 转换为我的 enum

为了便于比较,这是我在此应用程序的 iOS 版本中使用的 Swift 代码:

enum Venues: String, CaseIterable, Codable {
    case venue1 = "Venue 1"
    case venue2 = "Venue 2"
}

struct Tournament: Codable {
    var aString: String
    var aDate: Date
    var anInt: Int
    var venue: Venues
    var aBoolean: Bool
}

let tournamentsRef = Firestore.firestore().collection("tournaments")
var tournaments = [Tournament]()

tournamentsRef
    .addSnapshotListener { querySnapshot, error in
        guard let documents = querySnapshot?.documents else { return }

        self.tournaments = documents.compactMap { document in
            let tournament = try? document.data(as: Tournament.self)
            return tournament
        }
    }

func createNewTournament() {
    let newTournament = Tournament(aString: "String", aDate: Date(), anInt: 8, venue: Venues.venue1, aBoolean: true)

    do {
        let _ = try tournamentsRef.addDocument(from: newTournament)
    } catch {
        print (error)
    }
}

总而言之,snapshot.toObjects() 无法开箱即用地反序列化非基元。

通过使用 @Parcelize@Serializable 注释枚举,这将在后台生成代码,因此它知道如何为您的自定义 class 执行此操作。两者都需要 gradle 依赖项,分别是 org.jetbrains.kotlin.plugin.parcelizeorg.jetbrains.kotlinx:kotlinx-serialization

对于枚举,例如:

enum class Venues (val nickname: String) {
    Venue1 ("Venue 1"),
    Venue2 ("Venue 2")
}
如果没有特别定义,

nickname 不使用。 Venue1 将被序列化,Venue1