You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Kotlin从Firebase取数反序列化得到HashMap而非列表报错如何解决

问题原因分析

你遇到的Expected a List while deserializing, but got a class java.util.HashMap报错,本质是Firebase Realtime Database的反序列化规则导致的:当数据库中的类数组结构存在索引不连续、或者存在非数字键时,Firebase不会将其解析为List/ArrayList,而是会转为HashMap结构,你定义的Category类中specialities字段为ArrayList类型,反序列化时类型不匹配就抛出了该错误。

当前手写Map解析的问题

你现在的手写解析方案可以正常运行,但存在以下可优化点:

  • 未处理specialities字段:当前构造Category实例时没有为specialities赋值,该字段始终为默认的空ArrayList,和你单独存储的全局specialities集合完全脱节,后续需要从Category实例取专业数据时会拿到空值。
  • 容错率低:使用getValue读取Map字段时,如果对应键缺失会直接抛出异常,生产环境下如果某条分类数据缺少字段会直接崩溃。
  • 类型转换不严谨:将数字类型的lastUpdated、order先转为String再转目标类型,存在不必要的转换开销,也可能因为格式问题抛出转换异常。
优化方案

方案1:结合Firebase自动反序列化+手动处理嵌套字段(更推荐)

首先给Category类添加Firebase的@IgnoreExtraProperties注解,避免多余字段干扰反序列化:

@IgnoreExtraProperties
@Entity(tableName = "categories_table")
data class Category(
    var details: String = "",
    @PrimaryKey
    var id: String = "",
    var lastUpdated: Long = -1,
    var name: String = "",
    var order: Int = -1,
    val specialities: ArrayList<String> = arrayListOf()
) 

然后调整数据读取逻辑:

val valueEventListener = object : ValueEventListener {
    override fun onDataChange(snapshot: DataSnapshot) {
        categories.clear()
        specialities.clear()
        for (cat in snapshot.children){
            // 自动反序列化基础字段
            val category = cat.getValue(Category::class.java) ?: continue
            // 手动处理specialities节点,兼容HashMap和List两种结构
            val specList = arrayListOf<String>()
            cat.child("specialities").children.forEach { specSnap ->
                specSnap.getValue(String::class.java)?.let { specList.add(it) }
            }
            // 给Category实例赋值specialities
            val finalCategory = category.copy(specialities = specList)
            categories.add(finalCategory)
            specialities.addAll(specList)
        }
        _categories.value = categories
    }

    override fun onCancelled(error: DatabaseError) {
        throw error.toException()
    }
}

categoryRef.addListenerForSingleValueEvent(valueEventListener)

方案2:优化手写Map解析逻辑

如果你要继续使用手动解析的方式,可以调整为以下容错性更高的写法:

for (cat in snapshot.children) {
    val categoryMap = cat.value as? Map<String, Any> ?: continue
    // 空安全读取字段,缺失时用默认值填充
    val details = categoryMap["details"]?.toString() ?: ""
    val name = categoryMap["name"]?.toString() ?: ""
    val id = categoryMap["id"]?.toString() ?: ""
    val lastUpdated = (categoryMap["lastUpdated"] as? Long) ?: -1L
    val order = (categoryMap["order"] as? Int) ?: -1
    // 读取specialities字段,兼容HashMap结构
    val specialitiesList = arrayListOf<String>()
    (categoryMap["specialities"] as? Map<*, *>)?.values?.forEach { spec ->
        spec?.toString()?.let { specialitiesList.add(it) }
    }
    val category = Category(
        details = details,
        id = id,
        lastUpdated = lastUpdated,
        name = name,
        order = order,
        specialities = specialitiesList
    )
    categories.add(category)
    specialities.addAll(specialitiesList)
}

内容的提问来源于stack exchange,提问作者Serhii Bilotserkovets

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.25 08:45:04