Home > OS >  How to parse objects that has a sealed class param in kotlin android development using Room?
How to parse objects that has a sealed class param in kotlin android development using Room?

Time:01-29

I have a Plant data class, that has a PlantType sealed class parameter. I am using a Room local db, but when I try to parse it it fails. It works for other classes that has initializable class parameters.

Thanks for the help in advance.

The error:
java.lang.RuntimeException: Failed to invoke private com.tenyitamas.mylittlegarden.domain.util.PlantType() with no args

at com.tenyitamas.mylittlegarden.data.util.Converters.fromPlantsJson(Converters.kt:99)

// Comment: Converters.kt: 99 is the from json part of the Converters code, I included.

Plant.kt:

data class Plant(
    val type: PlantType,
    val upgrades: Upgrades
)

PlantType.kt:

sealed class PlantType {

    object Carrot : PlantType()
    object Tomato : PlantType()
    object Cucumber : PlantType()
    object Lettuce : PlantType()
    object Strawberry : PlantType()

    private companion object Constants {
        const val INITIAL_TIME_CARROT = 5_000L // 5s
        const val INITIAL_TIME_TOMATO = 6_000L
        const val INITIAL_TIME_CUCUMBER = 7_000L
        const val INITIAL_TIME_LETTUCE = 8_000L
        const val INITIAL_TIME_STRAWBERRY = 9_000L

        const val INITIAL_COST_CARROT = 10
        const val INITIAL_COST_TOMATO = 100
        const val INITIAL_COST_CUCUMBER = 1_000
        const val INITIAL_COST_LETTUCE = 10_000
        const val INITIAL_COST_STRAWBERRY = 100_000

        const val INITIAL_INCOME_CARROT = 10
        const val INITIAL_INCOME_TOMATO = 100
        const val INITIAL_INCOME_CUCUMBER = 1_000
        const val INITIAL_INCOME_LETTUCE = 10_000
        const val INITIAL_INCOME_STRAWBERRY = 100_000
    }

    val name: String
        get() {
            return when (this) {
                Carrot -> Resources.getSystem().getString(R.string.carrot)
                Tomato -> Resources.getSystem().getString(R.string.tomato)
                Cucumber -> Resources.getSystem().getString(R.string.cucumber)
                Lettuce -> Resources.getSystem().getString(R.string.lettuce)
                Strawberry -> Resources.getSystem().getString(R.string.strawberry)
            }
        }

    val image: Bitmap
        get() {
            return when (this) {
                Carrot -> {
                    BitmapFactory.decodeResource(Resources.getSystem(), R.drawable.ic_carrot)
                }
                Tomato -> {
                    BitmapFactory.decodeResource(Resources.getSystem(), R.drawable.ic_tomato)
                }
                Cucumber -> {
                    BitmapFactory.decodeResource(Resources.getSystem(), R.drawable.ic_cucumber)
                }
                Lettuce -> {
                    BitmapFactory.decodeResource(Resources.getSystem(), R.drawable.ic_lettuce)
                }
                Strawberry -> {
                    BitmapFactory.decodeResource(Resources.getSystem(), R.drawable.ic_strawberry)
                }
            }
        }

    val initialTime: Long
        get() {
            return when (this) {
                Carrot -> INITIAL_TIME_CARROT
                Tomato -> INITIAL_TIME_TOMATO
                Cucumber -> INITIAL_TIME_CUCUMBER
                Lettuce -> INITIAL_TIME_LETTUCE
                Strawberry -> INITIAL_TIME_STRAWBERRY
            }
        }

    val initialCost: Int
        get() {
            return when (this) {
                Carrot -> INITIAL_COST_CARROT
                Tomato -> INITIAL_COST_TOMATO
                Cucumber -> INITIAL_COST_CUCUMBER
                Lettuce -> INITIAL_COST_LETTUCE
                Strawberry -> INITIAL_COST_STRAWBERRY
            }
        }

    val initialIncome: Int
        get() {
            return when (this) {
                Carrot ->   INITIAL_INCOME_CARROT
                Tomato ->   INITIAL_INCOME_TOMATO
                Cucumber -> INITIAL_INCOME_CUCUMBER
                Lettuce ->  INITIAL_INCOME_LETTUCE
                Strawberry -> INITIAL_INCOME_STRAWBERRY
            }
        }
}

Converters.kt

@TypeConverter
    fun fromPlantsJson(json: String): List<Plant> {
        return jsonParser.fromJson<ArrayList<Plant>>(
            json,
            object : TypeToken<ArrayList<Plant>>(){}.type
        ) ?: emptyList()
    }

    @TypeConverter
    fun toPlantsJson(plants: List<Plant>): String {
        return jsonParser.toJson(
            plants,
            object : TypeToken<ArrayList<Plant>>(){}.type
        ) ?: "[]"
    }

CodePudding user response:

Your PlantType sealed class has only objects inside it. You can just use an enum for this.

enum class PlantType {
    Carrot, Tomato, Cucumber, Lettuce,  Strawberry
}

And most likely your jsonParser will be able to serialize this enum.

Edit: In your edited question, you just have added some new vals inside the PlantType class all of which has custom getters which return value depending upon the PlantType. You can still replace it with the enum and put those vals as extensions on PlantType.

For example,

enum class PlantType {
    Carrot, Tomato, Cucumber, Lettuce,  Strawberry
}

val PlantType.name: String
        get() {
            return when (this) {
                Carrot -> Resources.getSystem().getString(R.string.carrot)
                Tomato -> Resources.getSystem().getString(R.string.tomato)
                Cucumber -> Resources.getSystem().getString(R.string.cucumber)
                Lettuce -> Resources.getSystem().getString(R.string.lettuce)
                Strawberry -> Resources.getSystem().getString(R.string.strawberry)
            }
        }
  •  Tags:  
  • Related