如何将Firestore文档聚合数据映射为Kotlin List
解决Firestore聚合数组转自定义数据类列表的类型转换问题
为减少Firestore读取次数,我在helperz集合中创建top10文档,通过单次读取获取10条记录,文档包含tresholdCounter字段和top10(Map数组)字段。定义MyClass数据类后,尝试将top10数组直接转换为ArrayList<MyClass>时出现类型转换错误,单个文档可通过toObject(MyClass::class.java)正常转换,但数组场景无法直接转换。
核心原因
Firestore返回的top10数组本质是List<Map<String, Any>>,不能直接强转为ArrayList<MyClass>,必须逐个将数组中的每个Map对象手动映射为MyClass实例。
具体实现代码
在ViewModel中读取文档并转换数组的示例代码:
import com.google.firebase.firestore.FirebaseFirestore import com.google.firebase.firestore.PropertyName import com.google.firebase.firestore.ServerTimestamp import com.google.firebase.Timestamp // 定义字段常量,确保与Firestore文档字段名一致 const val CREATED = "created" data class MyPoze(/* 你的MyPoze数据类定义 */) data class MyClass( var name: String? = null, var url: String? = null, var author: String? = null, var counter: Int = 0, var free: Boolean? = false, var global: Boolean? = true, var poses: ArrayList<MyPoze>? = null, var docId: String? = null, // 修正原代码中的小写string为大写String var category: ArrayList<String>? = null, @get:PropertyName(CREATED) @set:PropertyName(CREATED) @ServerTimestamp var created: Timestamp? = null ) // ViewModel中的读取逻辑 fun loadTop10Data() { val db = FirebaseFirestore.getInstance() db.collection("helperz").document("top10") .get() .addOnSuccessListener { docSnapshot -> if (docSnapshot.exists()) { // 安全获取top10数组字段,避免类型转换异常 val top10Maps = docSnapshot.get("top10") as? List<Map<String, Any>> ?: emptyList() // 转换为ArrayList<MyClass> val top10List = arrayListOf<MyClass>() top10Maps.forEach { map -> val myClassItem = MyClass().apply { name = map["name"] as? String url = map["url"] as? String author = map["author"] as? String counter = map["counter"] as? Int ?: 0 // 给基本类型设置默认值 free = map["free"] as? Boolean ?: false global = map["global"] as? Boolean ?: true // 处理嵌套的poses数组(根据MyPoze字段手动映射) val posesMaps = map["poses"] as? List<Map<String, Any>> poses = posesMaps?.mapNotNull { poseMap -> MyPoze().apply { // 示例:poseId = poseMap["poseId"] as? String } } as? ArrayList<MyPoze> docId = map["docId"] as? String category = map["category"] as? ArrayList<String> created = map[CREATED] as? Timestamp } top10List.add(myClassItem) } // 将转换后的列表用于更新UI,比如赋值给LiveData // _top10LiveData.value = top10List } } .addOnFailureListener { e -> // 处理读取失败,比如打印错误日志 e.printStackTrace() } }
注意事项
- 空安全处理:所有字段转换使用
as?进行安全类型转换,给基本类型字段设置默认值,避免空指针异常。 - 嵌套对象/数组:如果
MyClass包含嵌套自定义对象(如MyPoze),需要对嵌套数组进行二次遍历转换。 - 字段名匹配:确保Firestore文档字段名与
MyClass属性名一致,使用@PropertyName注解时,注解值需与Firestore字段名完全匹配。 - 数据类型一致性:Firestore字段类型要与
MyClass属性类型对应,比如整数对应Int、布尔值对应Boolean等。
内容的提问来源于stack exchange,提问作者Gregor Sotošek
相关产品推荐
相关产品推荐

