CJR
CJR

Reputation: 3572

Error when using TypeConverter with Room: Cannot figure out how to save this field into database

I get this error:

error: Cannot figure out how to save this field into database. You can consider adding a type converter for it. private final java.util.List<com.example.Detail.Stat> stats = null;

I can't figure it out. I have Added the typeconverter to the database, but still get this error. Any ideas what I do wrong?

Entity:

@Entity
data class Detail(
    @PrimaryKey val id: Int,
    val stats: List<Stat>,
    val types: List<String>

){
    data class Stat(
        val baseStat: Int,
        val stat: String
    )
}

Typeconverter:

@ProvidedTypeConverter
class StatConverter @Inject constructor(
    private val moshi: Moshi
){
    @TypeConverter
    fun fromJson(value: String): List<Detail.Stat>? {
        val listType = Types.newParameterizedType(List::class.java, Detail.Stat::class.java)
        val adapter: JsonAdapter<List<Detail.Stat>> = moshi.adapter(listType)
        return adapter.fromJson(value)
    }
    @TypeConverter
    fun toJson(type: List<Detail.Stat>?): String {
        val listType = Types.newParameterizedType(List::class.java, Detail.Stat::class.java)
        val adapter: JsonAdapter<List<Detail.Stat>> = moshi.adapter(listType)
        return adapter.toJson(type)
    }
}

Database:

@Database(entities = [Detail::class], version = 1, exportSchema = true)
@TypeConverters(StatConverter::class)
abstract class Database : RoomDatabase() {

    abstract fun detailDao(): DetailDao

    companion object{
        const val DATABASE = "database"
    }

}

DI module where room is provided:

@Singleton
    @Provides
    fun provideAppDatabase(
        application: Application,
        statConverter: StatConverter
    ): Database {
        return Room
            .databaseBuilder(application, Database::class.java,
                Database.DATABASE
            )
            .addTypeConverter(statConverter)
            .fallbackToDestructiveMigration()
            .build()
    }

EDIT:

The typeconverter code works fine with the other field (List) in the entity, but not with List.

Upvotes: 2

Views: 1268

Answers (3)

charles_ganza
charles_ganza

Reputation: 87

To anyone who stumbles upon this, if you're using KSP, it's super important to make sure the field type matches the return type of the converter method. Even the nullability has to match.

For example, this will work:

Converter class:

@ProvidedTypeConverter
class SongListTypeAdapter {

    // json adapter init omitted for brevity

    @TypeConverter
    fun songsListToString(songs: List<Station>): String {
        return jsonAdapter.toJson(stations)
    }
    @TypeConverter
    fun songsStringToList(songs: String): List<Song> {
        return jsonAdapter.fromJson(songs) ?: emptyList()
    }
}

Entity class:

@Entity(tableName = "playlist")
class Playlist: Serializable {
    @PrimaryKey(autoGenerate = true)
    var id = 0

    @field:TypeConverters(SongListTypeAdapter::class)
    @ColumnInfo(name = "songs") var songs: List<Song> = listOf()
}

This won't work:

@ProvidedTypeConverter
class SongListTypeAdapter {

    // json adapter init omitted for brevity

    @TypeConverter
    fun songsListToString(songs: List<Station>): String {
        return jsonAdapter.toJson(stations)
    }
    @TypeConverter
    fun songsStringToList(songs: String): List<Song>? { // issue is here, it's nullable while the actual songs field is not
        return jsonAdapter.fromJson(songs)
    }
}

@Entity(tableName = "playlist")
class Playlist: Serializable {
    @PrimaryKey(autoGenerate = true)
    var id = 0

    @field:TypeConverters(SongListTypeAdapter::class)
    @ColumnInfo(name = "songs") var songs: List<Song> = listOf()
}

Notice how the return type in the second example is nullable. KSP will throw an error and you'll need to make sure the types match perfectly.

Upvotes: 1

CommonsWare
CommonsWare

Reputation: 1006674

Apparently, something about your nested data class is causing problems, and so moving Stat out from being nested in Detail helped.

If you have the time, you might try creating a scrap project that illustrates the problem, then file an issue on the issue tracker, attaching that project as a demonstration of the problem. I don't see anything that quite matches, but there are a lot of issues, so perhaps I missed it.

Upvotes: 1

georgiecasey
georgiecasey

Reputation: 23381

I didn't run your code or test this out, but from eyeballing it here, is it possible it's the difference between nullable List<Detail.Stat>? in the type converter and non-nullable List<Stat> in the entity? Either make entity nullable or type-converter non-nullable and see if it works.

Upvotes: 1

Related Questions