Firebase实时数据库新增子节点时类型转换异常求助
解决Firebase实时数据库新增子节点时的类型转换错误
错误信息
新增子节点时触发应用崩溃,抛出异常:
Process: com.example.myApp, PID: 12458 com.google.firebase.database.DatabaseException: Can't convert object of type java.lang.String to type com.example.myApp.model.NoticeModel
数据库结构
当前数据库结构如下:
{ "newsNotices": { "dateChanged": { "lastUpdated": "01 January 2024 14:19:18" }, "eventItem": { "-NmSeiOMCcp1qeQmj7Mu": { "id": 1703452272, "notice": { "name": "Event Title", "sub_list": [ { "link": "https://www.antennahouse.com/hubfs/xsl-fo-sample/pdf/basic-link-1.pdf", "name": "Poster" }, { "link": "https://www.antennahouse.com/hubfs/xsl-fo-sample/pdf/basic-link-1.pdf", "name": "Brochure" } ] } }, "-NmSelGBrZqTB2kFPHC4": { "id": 1703452284, "notice": { "link": "https://www.antennahouse.com/hubfs/xsl-fo-sample/pdf/basic-link-1.pdf", "name": "Workshop link" } } }, "running_time": { "lastRun": 1704191244 } } }
需获取eventItem下的数据,每个子项包含id和notice节点,notice支持两种结构:name+link或name+sub_list。
数据类定义
用于反序列化的数据类:
data class NoticeModel( var id: Long?=null, var notice: newsModel? = null ) data class newsModel( var link: String? = null, var name: String? = null, var sub_list: List<SubList?>? = null ) data class SubList( val link: String? = null, val name: String? =null )
仓库与ViewModel代码
仓库类
class MainRepository { private val database = Firebase.database fun fetchNews(noticeLiveData: MutableLiveData<Response<List<NoticeModel>>>, path: String) { val newsReference = database.getReference(path) newsReference.addValueEventListener(object : ValueEventListener { override fun onDataChange(snapshot: DataSnapshot) { val newsItemList = mutableListOf<NoticeModel>() for (ds in snapshot.children) { val noticeModel = ds.getValue(NoticeModel::class.java) if (noticeModel != null) { newsItemList.add(noticeModel) } } noticeLiveData.postValue(Response.Success(newsItemList)) } override fun onCancelled(error: DatabaseError) { noticeLiveData.postValue(Response.Error("Some error occurred")) } }) } }
ViewModel类
class EventsViewModel : ViewModel() { private val repository = MainRepository() private val _latestEventsLiveData = MutableLiveData<Response<List<NoticeModel>>>() val latestEventsLiveData: LiveData<Response<List<NoticeModel>>> = _latestEventsLiveData private val path = "/newsNotices/eventItem" fun fetchLatestNews() { repository.fetchNews(_latestEventsLiveData, path) } }
问题原因
错误核心是新增的子节点结构不符合NoticeModel的预期:
- 可能在
eventItem下直接添加了字符串类型的节点,而非包含id和notice的对象 - 新增的
notice节点被错误设置为字符串,而非对象结构 - 写入数据时路径错误,导致监听的
snapshot中混入了非目标结构的数据
解决方案
1. 严格校验新增子节点的结构
确保所有新增到eventItem下的子节点都遵循以下结构:
{ "id": 1703452284, "notice": { "name": "示例标题", "link": "示例链接" // 或 "name": "带子列表的标题", "sub_list": [ {"name": "子项1", "link": "链接1"}, {"name": "子项2", "link": "链接2"} ] } }
禁止在eventItem下直接写入字符串、数字等非对象类型的数据。
2. 代码中添加异常捕获
修改仓库类的onDataChange方法,增加异常捕获,避免单个错误节点导致整个列表加载崩溃:
override fun onDataChange(snapshot: DataSnapshot) { val newsItemList = mutableListOf<NoticeModel>() for (ds in snapshot.children) { try { val noticeModel = ds.getValue(NoticeModel::class.java) noticeModel?.let { newsItemList.add(it) } } catch (e: DatabaseException) { Log.e("FirebaseParseError", "解析节点 ${ds.key} 失败: ${e.message}") // 跳过错误节点,不影响其他正常数据的加载 } } noticeLiveData.postValue(Response.Success(newsItemList)) }
3. 确保数据类与数据库字段匹配
检查数据类字段名与数据库字段完全一致(大小写敏感),比如newsModel中的sub_list必须和数据库的sub_list字段名完全相同;所有可空字段保留?=null的默认值,保证Firebase能正确反序列化缺失字段的场景。
4. 验证数据写入逻辑
如果通过代码写入数据,确保调用setValue()时传入的是NoticeModel对象,而非字符串:
// 正确写法 val newEventRef = database.getReference("/newsNotices/eventItem").push() val newNotice = newsModel(name = "新活动", link = "活动链接") newEventRef.setValue(NoticeModel(id = System.currentTimeMillis(), notice = newNotice)) // 错误写法(会导致子节点变成字符串,触发类型转换错误) // newEventRef.setValue("错误的字符串内容")
内容的提问来源于stack exchange,提问作者Navi_Here
相关产品推荐
相关产品推荐

