Android: How to make type converters (for Room) generic for all List of objects in Kotlin

Viewed 4711

I am using Room as the local database solution in my project. For every list of a certain object type I've added type converters to the project so the type convertor would look something like this:

@TypeConverter
fun convertListToString(video: List<VideoType>): String {

    val videoArray = arrayOfNulls<VideoType>(video.size)
    for (i in 0..video.size - 1) {
        videoArray[i] = video[i]
    }
    var str = ""
    val gson = Gson()
    for (i in videoArray.indices) {
        val jsonString = gson.toJson(videoArray[i])
        str = str + jsonString
        if (i < videoArray.size - 1) {
            str = str + strSeparator
        }
    }

    return str
}

@TypeConverter
fun convertStringToList(videoString: String): List<VideoType> {

    val videoArray = videoString.split(strSeparator.toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()
    val videos = ArrayList<VideoType>()
    val gson = Gson()
    for (i in 0 until videoArray.size - 1) {
        videos.add(gson.fromJson(videoArray[i], VideoType::class.java))
    }

    return videos
}

Problem only is that I have a whole bunch of List of different types that need to be converted, so currently I am just copying this code for every type. I would like to use generics, but so far haven't been able to figure out how to do it.

For example using something like:

@TypeConverter
inline fun <reified T> convertStringToList(string: String): List<T> {
    val objectArray = string.split(strSeparator.toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()
    val objects = ArrayList<T>()
    val gson = Gson()
    for (i in 0 until objectArray.size - 1) {
        objects.add(gson.fromJson(objectArray[i], T::class.java))
    }
    return objects
}

isn't working and causes compilation error with Android Studio giving me an error telling: Type converters must be public

Anyone an idea how I can use generics for my Room TypeConverter?

5 Answers

Will something like this won't work for templating?

abstract class Converters<T> {

    @TypeConverter
    fun mapListToString(value: List<T>): String {
        val gson = Gson()
        val type = object : TypeToken<List<T>>() {}.type
        return gson.toJson(value, type)
    }

    @TypeConverter
    fun mapStringToList(value: String): List<T> {
        val gson = Gson()
        val type = object : TypeToken<List<T>>() {}.type
        return gson.fromJson(value, type)
    }
}

// create 
class UserConverter : Converters<User>()

// usage
@TypeConverters(UserConverter::class)
abstract class TestDatabase : RoomDatabase() {
    abstract fun userDao(): UserDao
}

or from the current example what if I do

@TypeConverter
fun <T> mapStringToList(inputString: String): List<T> {
    val array = inputString
        .split(strSeparator.toRegex())
        .dropLastWhile { it.isEmpty() }
        .toTypedArray()
    val result = mutableListOf<T>()
    val gson = Gson()
    //  ----
    val type = object : TypeToken<List<T>>() {}.type
    // ----
    return array.fold(result) { acc, item ->
        acc.add(gson.fromJson(item, type))
        acc
    }
}

I have same problem. Partially solution just for converting listToString

@TypeConverter
@JvmStatic
fun listToString(list: List<*>?): String? = list.let {
    val gson = GsonBuilder().serializeNulls().create()

    gson.toJson(it)
}

I tried to use a marker interface to convert classes to marker and vice versa but it didn't worked. I tried the Object class which most of Java extends, but it didn't work. I think the answer is that you can't use Generic in room typeconvertors.

Well, basically the main idea of type converters is that databases can't read objects as a parameters.So you've to 2 ways first one is to do it manually with join relation inside the db, Or you can use type converter. How type converter works? It's simple it just convert your object to JSON String and save it as string. In your problem, you've to add annotation before entity

private class YourDTO(
        @TypeConverters(DateConverter.class)
         Date date

And in DateConverter class

@TypeConverter
public static Long toTimestamp(Date date) {
    return date == null ? null : date.getTime();
}

I just tested this out, seems to do the base for what you'd be looking for. Up to you to tweak the function to your needs, but that should get you going : Reference https://kotlinlang.org/docs/reference/generics.html#generic-functions

fun test() {
        val list1 = listOf("1", "2")
        val list2 = listOf(1, 2, 3)

        list1.convertToString()
        list2.convertToString()
    }

    fun List<Any>.convertToString(): String {
        var result = ""
        val gson = Gson()
        for (i in 0.. size - 1) {
            result += gson.toJson(get(i), get(i).javaClass)
        }
        return result
    }
Related