Home > Blockchain >  Kotlin reflection
Kotlin reflection

Time:05-25

With Retrofit, I am fetching data and getting JSONObject with the structure like this for example:

{
    "token": "some-token",
    "tiles": [
        {
            "id": "id-1",
            "type": "RedTile",
            "title": "This red title",
            "stuff": "hello world"
        },
        {
            "id": "id-2",
            "type": "BlueTile",
            "title": "This blue title",
            "value": 200
        }
    ]
}

Now, when I get this response, I need to make a parser that will actually take the type from every tile and match it with the class that already exists inside application. I need to basically from this response to create new one with token and tiles list but in this case tiles will reflect to actual class in app.

Any idea what is proper way to do it?!

CodePudding user response:

I don't know what your classes look like, but suppose it's this for example:

sealed interface Tile
data class RedTile(val title: String, val stuff: String) : Tile
data class BlueTile(val title: String, val value: Int) : Tile

Then you could make this as response object

data class Response (
    val token: String,
    val tiles: List<TileResponse>
) {
    fun getTilesObjects() = tiles.mapNotNull { it.toTile() }
}

data class TileResponse (
    val id: String,
    val type: String,
    val title: String,
    val stuff: String? = null,
    val value: Int? = null
){
    fun toTile() : Tile? {
        if (type == "RedTile" && stuff != null) return RedTile(title, stuff)
        if (type == "BlueTile" && value != null) return BlueTile(title, value)
        return null
    }
}

And then you can call toTile() on the individual TileResponse objects or get the full list by doing getTilesObjects() on the response object

CodePudding user response:

Because you mention Reflection in your question's title:

import kotlin.reflect.full.declaredMemberProperties

data class TileResponse(val id: String, val type: String, val title: String, val stuff: String? = null, val value: Int? = null)
data class Response(val token: String, val tiles: List<Tile>)

sealed interface Tile
data class RedTile(val title: String, val stuff: String) : Tile
data class BlueTile(val title: String, val value: Int) : Tile

fun getTiles(): List<Tile> {
  return tileResponses
    .filter { tileResponse -> mapTypeToClass.containsKey(tileResponse.type) }
    .map { tileResponse ->
      val clazz = mapTypeToClass[tileResponse.type]!!
      val constructor = clazz.constructors.first()
      val declaredMemberProperties = tileResponse::class.declaredMemberProperties
      val fields = constructor.parameters.map { parameter ->
        parameter to declaredMemberProperties.first { it.name == parameter.name }
      }
      val arguments = fields.associate { (parameter, property) ->
        parameter to property.call(tileResponse)
      }
      constructor.callBy(arguments)
    }
}

val tileResponses = listOf(
  TileResponse("id-1", "RedTile", "This red title", "hello world", null),
  TileResponse("id-2", "BlueTile", "This blue title", "null", 200)
)

val mapTypeToClass = mapOf(
  "RedTile" to RedTile::class,
  "BlueTile" to BlueTile::class,
)

val response = Response("some-token", getTiles())
  • Related