3

Given a simple data class like:

data class TestSimple(
    val country: String,
    var city: String? = null,
    var number: Int? = null,
    var code: Long? = null,
    var amount: Float? = null,
    var balance: Double? = null
)

Is there any way I can use kotlin-reflect to find the data type of the properties? I got all the properties via:

val allFields = this::class.declaredMemberProperties.map {
    it.name to it
}.toMap()

I only got as far as allFields["number"].returnType which returns a KType. I couldn't figure out a way to check if a KType is Int or Long.

I am trying to avoid the code I currently use to cast the incoming JSON numeric data to appropriate data type:

fun castToLong(value: Any): Long {
    val number = try {
        value as Number
    } catch (e: Exception) {
        throw Exception("Failed to cast $value to a Number")
    }
    return number.toLong()
}
marcoseu
  • 3,892
  • 2
  • 16
  • 35

1 Answers1

2

First, you can use some library to parse JSON to actual types. Jackson has good Kotlin support. If you don't want to use library, to determine type of parameter you can use this snippet:

import java.time.OffsetDateTime
import kotlin.reflect.KClass
import kotlin.reflect.full.declaredMemberProperties

data class UpdateTaskDto(
        val taskListId: Long,
        val name: String,
        val description: String? = null,
        val parentTaskId: Long? = null,
        val previousTaskId: Long? = null,
        val dateFrom: OffsetDateTime? = null,
        val dateTo: OffsetDateTime? = null,
        val dateOnlyMode: Boolean? = false
) {
    fun test() {
        this::class.declaredMemberProperties.forEach { type ->
            println("${type.name} ${type.returnType.classifier as KClass<*>}")
        }
    }
}

As a result of calling test method, I got:

dateFrom class java.time.OffsetDateTime
dateOnlyMode class kotlin.Boolean
dateTo class java.time.OffsetDateTime
description class kotlin.String
name class kotlin.String
parentTaskId class kotlin.Long
previousTaskId class kotlin.Long
taskListId class kotlin.Long
mregulski
  • 160
  • 2
  • 8
  • thanks for the answer. I managed to get result you suggested but I cannot figure out a way to cast to the result KClass. Any ideas? E.g.: "val clazz = prop.returnType.classifier as KClass<*>; val result = value as clazz"? In this case `value` is type Any. – marcoseu Feb 01 '19 at 19:27
  • Ok, I could make `clazz is Long` to work so resorted to `clazz.simpleName == "Long"`. Not ideal but works for now. Thanks! – marcoseu Feb 01 '19 at 20:27
  • What exactly do you want to do with this information? – mregulski Feb 02 '19 at 14:22
  • Per "castToLong" function I posted, I looking for a way to create something like "inline fun castTo(value: Any, type: T): T" , or variation of it so I can avoid creating "castToInt", "castToLong", etc. – marcoseu Feb 02 '19 at 18:45
  • If I understand you well, you would like to parse JSON data so you probably do not want to cast String to other types. You need to provide converter, e. g. https://github.com/FasterXML/jackson-databind/blob/master/src/main/java/com/fasterxml/jackson/databind/deser/std/NumberDeserializers.java – mregulski Feb 02 '19 at 19:05
  • Thanks but my problem is not with JSON parsing and I am not trying to cast a String to a Number per my "castToLong" function. The problem is that JSON returns a Map and often the number is parsed to an Int. This become a problem when I want to get that Int into a Long property. In my specific usage scenario, I receive huge messages with lots of stuff I simply do not need; all I need is couple of specific numeric fields out of that JSON message. – marcoseu Feb 02 '19 at 19:21
  • What about this method? https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.reflect.full/cast.html – mregulski Feb 02 '19 at 19:24
  • Nope... Tried that. Same error if I just tried to put an Any with Int in it to a Long property. – marcoseu Feb 02 '19 at 19:27
  • You can use https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.reflect/-k-class/is-instance.html and safeCast method. I think this is all you need. First check if your property is one from your data class and cast to appropriate type. – mregulski Feb 02 '19 at 19:34
  • Yes, thank you. That's exactly what I am doing right now. – marcoseu Feb 03 '19 at 11:51