Is there any easy way or any standard library method to convert a Kotlin data class object to a map/dictionary of its properties by property names? Can reflection be avoided?
6 Answers
I was using the jackson method, but turns out the performance of this is terrible on Android for first serialization (github issue here). And its dramatically worse for older android versions, (see benchmarks here)
But you can do this much faster with Gson. Conversion in both directions shown here:
import com.google.gson.Gson
import com.google.gson.reflect.TypeToken
val gson = Gson()
//convert a data class to a map
fun <T> T.serializeToMap(): Map<String, Any> {
return convert()
}
//convert a map to a data class
inline fun <reified T> Map<String, Any>.toDataClass(): T {
return convert()
}
//convert an object of type I to type O
inline fun <I, reified O> I.convert(): O {
val json = gson.toJson(this)
return gson.fromJson(json, object : TypeToken<O>() {}.type)
}
//example usage
data class Person(val name: String, val age: Int)
fun main() {
val person = Person("Tom Hanley", 99)
val map = mapOf(
"name" to "Tom Hanley",
"age" to 99
)
val personAsMap: Map<String, Any> = person.serializeToMap()
val mapAsPerson: Person = map.toDataClass()
}

- 1,252
- 11
- 21
-
How to use these functions ? Can you show some code ? – Serge Tahé Nov 03 '20 at 18:54
-
sure, I've added some example usage to the answer – Tom Hanley Nov 05 '20 at 17:52
-
I tried your code and I got two nulls for [personAsMap] and [mapAsPerson] as i got before my comment. That's why i asked some examples. Is this code working for you ? I am using Kotlin 1.4.0 – Serge Tahé Nov 06 '20 at 08:37
-
Its definitely working fine for me. How are you trying to run it? Do you have gson in your class path? I've added the gson imports if that helps. I'm using kotlin 1.4 also and gson 2.8.6, but i don't think this is a version issue. – Tom Hanley Nov 07 '20 at 12:48
-
Hi Tom, i wanted to show the code executed. So i put it in an answer to the original question. Comments are to short for this. – Serge Tahé Nov 07 '20 at 18:55
-
Ok I tried out your code and got the same. Debugging, gson doesn't seem to work properly on local classes. If you move the person class declaration outside of your function, so its a top level class, it works. – Tom Hanley Nov 09 '20 at 11:27
-
1I don't recommend this solution. GSON doesn't work properly in Kotlin since it's unaware of its null vs. non-null Kotlin compile-time rules. This is a better solution but doesn't handle nested properties, only primitives: https://stackoverflow.com/a/59316850/4425374 – Jeremy Jao Dec 02 '20 at 04:00
-
in my case int convert to double example 1 -> 1.0 – Nalawala Murtuza Apr 07 '21 at 12:54
-
nested class int property converts to float property. – Nalawala Murtuza Apr 10 '21 at 10:54
This extension function uses reflection, but maybe it'll help someone like me coming across this in the future:
inline fun <reified T : Any> T.asMap() : Map<String, Any?> {
val props = T::class.memberProperties.associateBy { it.name }
return props.keys.associateWith { props[it]?.get(this) }
}

- 2,051
- 23
- 32
-
2Nice solution, although just a note: This does not look like it will be working for nested data structures. – Simon Forsberg Jan 04 '20 at 23:17
-
2
-
5
-
some java classes with `final` parameters like to throw exceptions like `java.lang.IllegalAccessException` so be sure to add `onEach { isAccessible = true }` after `memberProperties` – Ananiya Jemberu Sep 14 '22 at 19:38
-
For a nested structure, I simply write a specific function that adds a prefix for that property. Handling whole graphs of objects is already going into the field of deserialization, and at that point, you need a well-debugged, feature-rich library to do that. – Ondra Žižka Oct 04 '22 at 13:05
I have the same use case today for testing and ended up i have used Jackson object mapper to convert Kotlin data class into Map. The runtime performance is not a big concern in my case. I haven't checked in details but I believe it's using reflection under the hood but it's out of concern as happened behind the scene.
For Example,
val dataclass = DataClass(p1 = 1, p2 = 2)
val dataclassAsMap = objectMapper.convertValue(dataclass, object:
TypeReference<Map<String, Any>>() {})
//expect dataclassAsMap == mapOf("p1" to 1, "p2" to 2)

- 381
- 2
- 3
-
using above i am getting `No type arguments expected for class TypeReference` using jackson 2.10.0 – Ahsan Naseem Oct 02 '20 at 11:59
-
`No type arguments expected for class TypeReference` be sure, that you are using class `com.fasterxml.jackson.core.type.TypeReference` if you import for example `org.springframework.asm.TypeReference` you will get this message :) – bugs_ Jun 03 '21 at 08:11
kotlinx.serialization has an experimental Properties format that makes it very simple to convert Kotlin classes into maps and vice versa:
@ExperimentalSerializationApi
@kotlinx.serialization.Serializable
data class Category constructor(
val id: Int,
val name: String,
val icon: String,
val numItems: Long
) {
// the map representation of this class
val asMap: Map<String, Any> by lazy { Properties.encodeToMap(this) }
companion object {
// factory to create Category from a map
fun from(map: Map<String, Any>): Category =
Properties.decodeFromMap(map)
}
}

- 377
- 4
- 11
-
-
This seems to work only if the data class is flat and has no properties that are lists or maps. – erksch Sep 26 '21 at 21:49
The closest you can get is with delegated properties stored in a map.
Example (from link):
class User(val map: Map<String, Any?>) {
val name: String by map
val age: Int by map
}
Using this with data classes may not work very well, however.

- 5,331
- 27
- 34
-
2His problem is that he wants this the other way around: turn a `data class` into `Map
`. – EpicPandaForce Apr 16 '18 at 15:32 -
1@EpicPandaForce I'm aware, but there's no non-reflective way to do that in Kotlin. The closest alternative would be to use delegated properties in a map. (i.e. you'd assign values to the map in the constructor, rather than properties) – apetranzilla Apr 16 '18 at 15:36
-
Kpropmap is a reflection-based library that attempts to make working with Kotlin data classes and Maps easier. It has the following capabilities that are relevant:
- Can transform maps to and from data classes, though note if all you need is converting from a data class to a Map, just use reflection directly as per @KenFehling's answer.
data class Foo(val a: Int, val b: Int)
// Data class to Map
val propMap = propMapOf(foo)
// Map to data class
val foo1 = propMap.deserialize<Foo>()
Can read and write
Map
data in a type-safe way by using the data classKProperty
's for type information.Given a data class and a Map, can do other neat things like detect changed values and extraneous Map keys that don't have corresponding data class properties.
Represent "partial" data classes (kind of like lenses). For example, say your backend model contains a
Foo
with 3 required immutable properties represented asval
s. However, you want to provide an API to patchFoo
instances. As it is a patch, the API consumer will only send the updated properties. The REST API layer for this obviously cannot deserialize directly to theFoo
data class, but it can accept the patch as aMap
. Use kpropmap to validate that the Map has the correct types, and apply the changes from theMap
to a copy of the model instance:
data class Foo(val a: Int, val b: Int, val c: Int)
val f = Foo(1, 2, 3)
val p = propMapOf("b" to 5)
val f1 = p.applyProps(f) // f1 = Foo(1, 5, 3)
Disclaimer: I am the author.

- 17,606
- 5
- 95
- 112