简体   繁体   中英

Kotlin Data Class: how to set a property if I don't know its name at compile time?

So, I'm trying to set a property of a data class after compile time. I'm attempting to use bracket notation, but the IDE is barking at me with the following: No set method providing array access .

I did a little research, and it looks like I need to use the Reflection api. Tried using memberProperties , but after identifying the property there's no setter method on it. So, I'm here hoping to find some guidance.

Here's a contrived example of what I'm trying to do:

data class EmailMessageStats(
    var bounce: Int = 0,
    var click: Int = 0,
    var deferred: Int = 0,
    var delivered: Int = 0,
    var dropped: Int = 0,
    var open: Int = 0,
    var processed: Int = 0,
    var spamreport: Int = 0
)

val sampleResponseFromDatabase = listOf(
    mapOf("type" to "bounce", "value" to 148),
    mapOf("type" to "click", "value" to 142),
    mapOf("type" to "deferred", "value" to 286),
    mapOf("type" to "delivered", "value" to 700),
    mapOf("type" to "dropped", "value" to 152),
    mapOf("type" to "open", "value" to 550),
    mapOf("type" to "processed", "value" to 1000),
    mapOf("type" to "spamreport", "value" to 140)
)

var summary = EmailMessageStats()
for (row in sampleResponseFromDatabase) {
    val type = row["type"] // e.g. bounce
    val value = row["value"] //e.g. 148
    summary[type] = value
}

println(summary)

I made a Kotlin extension function just for that functionality:

fun Any.setPropertyValue(propName: String, value: Any) {
    for (prop in this::class.declaredMemberProperties) {
        if (prop.name == propName) {
            (prop as? KMutableProperty<*>)?.setter?.call(this, value)
        }
    }
}

Similarly for getting the property value:

fun Any.getPropertyValue(propName: String): Any? {
    for (prop in this::class.declaredMemberProperties) {
        if (prop.name == propName) return prop.getter.call(this)
    }
    return null
}

Here, you can set the property as follows:

var summary = EmailMessageStats()
for (row in sampleResponseFromDatabase) {
    val type = row["type"] // e.g. bounce
    val value = row["value"] //e.g. 148
    summary.setPropertyValue(type, value)
}

Keep in mind to use declaredMemberProperties if you only want the fields related to EmailMessageStats . Otherwise, if EmailMessageStats were to extend from another object, it will iterate through that object's fields too.

I got it worked in following way:

data class Item(var name: String, var description: String?)

On runtime

val item = "Runtime item object"
val property = "name"
val value = "Updated value of name"

val jo = JsonParser().parse( Gson().toJson(item) ).asJsonObject

if(jo.has(property)) {
    jo.remove(property)
    jo.addProperty(property, value)
}

val item = Gson().fromJson(jo, Item::class.java)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM