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

如何将指定JSON存入Room数据库?子类数据写入问题求助

解决Room无法存储嵌套子类数据的问题

Hey there! Let's figure out why you're having trouble saving nested data to Room, even though Retrofit is fetching it correctly. The core issue here is how your data classes are structured for Room compatibility—let's break this down step by step.

首先,你的现有代码存在几个关键问题:

  • 混乱的实体结构:你把顶层响应、业务数据条目和嵌套对象都塞进了同一个BookingModel类,还出现了自引用(@Ignore var data: BookingModel?),这会让 Room 和 Gson 都搞不清该怎么处理数据。
  • 未正确处理嵌套的coordinateBooking:这个子对象没有对应的实体类,也没通过@Embedded或合适的转换器来映射,Room 无法识别它的存储规则。
  • 错误的 TypeConverter:你写的转换器是针对List<BookingModel>的,自引用会导致循环序列化,而且这根本不是处理嵌套坐标对象的正确方式。

下面是修复方案,按步骤来:

1. 拆分数据类,明确职责

把顶层响应、业务数据、嵌套对象拆成独立的类——Room只需要存储业务数据(也就是booking_info里的条目),顶层响应类不用作为Room实体:

// 顶层Retrofit响应类(不用加@Entity)
data class BookingResponse(
    val code: Int,
    val sucsses: Boolean,
    val data: BookingData
)

// 包裹booking_info的中间类
data class BookingData(
    val booking_info: List<BookingItem>
)

// Room实体:对应booking_info里的每个条目
@Entity(tableName = "booking")
data class BookingItem(
    @PrimaryKey val _id: String, // 用接口返回的_id作为主键,避免重复存储
    val imageBooking: List<String>,
    val nameBooking: String,
    val descriptionBooking: String,
    val taskBooking: String,
    val priceBooking: Double?,
    val rateBooking: Double?,
    @Embedded val coordinateBooking: CoordinateBooking, // 用@Embedded嵌套坐标对象
    val clockWork: String?,
    val user_id: String
)

// 坐标嵌套类,用@Embedded可以直接把字段映射到booking表中
data class CoordinateBooking(
    val latitudeBooking: Long,
    val longitudeBooking: Long
)

2. 为List类型添加正确的TypeConverter

因为imageBooking是List<String>,Room无法直接存储,需要写对应的转换器:

class BookingConverters {
    @TypeConverter
    fun fromStringList(stringList: List<String>?): String? {
        return stringList?.let { Gson().toJson(it) }
    }

    @TypeConverter
    fun toStringList(jsonString: String?): List<String>? {
        return jsonString?.let {
            Gson().fromJson(it, object : TypeToken<List<String>>() {}.type)
        }
    }
}

3. 更新Room Database类

把实体改成BookingItem::class,并绑定我们的转换器:

@Database(
    entities = [BookingItem::class],
    version = 1,
    exportSchema = false
)
@TypeConverters(BookingConverters::class)
abstract class HelpCarDataBase : RoomDatabase() {
    abstract fun daoService(): DaoService
}

4. 调整DAO接口的插入方法

确保插入方法针对List<BookingItem>,并处理主键冲突:

@Dao
interface DaoService {
    @Insert(onConflict = OnConflictStrategy.REPLACE) // 重复主键时替换旧数据
    suspend fun insertBookings(bookings: List<BookingItem>)
}

5. 从Retrofit响应提取数据并插入Room

当Retrofit拿到数据后,提取业务条目插入Room:

// 示例:在ViewModel或Repository中处理
val retrofitResponse = bookingApi.getBookings()
if (retrofitResponse.isSuccessful && retrofitResponse.body() != null) {
    val bookingItems = retrofitResponse.body()!!.data.booking_info
    daoService.insertBookings(bookingItems)
}

为什么这样能解决问题?

  • @Embedded注解会把CoordinateBooking的字段直接合并到booking表中(比如生成latitudeBooking和longitudeBooking列),Room就能正确存储这些嵌套数据了。
  • 拆分数据类后,每个类的职责清晰,避免了自引用导致的序列化/存储混乱。
  • 用接口返回的_id作为主键,确保每条数据唯一,不会出现重复存储的问题。

内容的提问来源于stack exchange,提问作者Константин Щерба

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.27 07:03:18