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
相关产品推荐
相关产品推荐

