Hey Channel! How does one convert a Map<String,...
# getting-started
a
Hey Channel! How does one convert a Map<String, Any?> to data class? I’m trying to convert a
Map<String, Any?>
to a data class object using Kotlinx Serialisation. The Data Classes:
Copy code
import kotlinx.serialization.Serializable

@Serializable
data class Person(
    val uuid: String,
    val age: Int,
    val name: String,
    val address: Address
)

@Serializable
data class Address(
    val street: String,
    val city: String,
    val zip: Int
)
This is the function I use to convert to data class.
Copy code
import kotlinx.serialization.json.decodeFromJsonElement
import kotlinx.serialization.json.encodeToJsonElement
import kotlinx.serialization.json.jsonObject
import kotlinx.serialization.json.Json

internal inline fun <reified T : Any> Map<String, Any?>.toPojo(): T {
    val JSON = Json { encodeDefaults = true }
    val jsonObject = JSON.encodeToJsonElement(this).jsonObject
    return JSON.decodeFromJsonElement<T>(jsonObject)
}
Here’s the function call:
Copy code
val map = mapOf(
    "uuid" to "agsdjhdg",
    "age" to 26,
    "name" to "Aseem",
    "address" to mapOf(
        "city" to "Chennai",
        "street" to "street1"
    )
)

println(map.toPojo<Person>())
I get the following error:
Copy code
Exception in thread "main" kotlinx.serialization.SerializationException: Serializer for class 'Any' is not found.
Mark the class as @Serializable or provide the serializer explicitly.
	at kotlinx.serialization.internal.Platform_commonKt.serializerNotRegistered(Platform.common.kt:91)
	at kotlinx.serialization.internal.PlatformKt.platformSpecificSerializerNotRegistered(Platform.kt:29)
	at kotlinx.serialization.SerializersKt__SerializersKt.serializer(Serializers.kt:60)
	at kotlinx.serialization.SerializersKt.serializer(Unknown Source)
	at kotlinx.serialization.SerializersKt__SerializersKt.serializersForParameters(Serializers.kt:117)
	at kotlinx.serialization.SerializersKt.serializersForParameters(Unknown Source)
	at kotlinx.serialization.SerializersKt__SerializersKt.serializerByKTypeImpl$SerializersKt__SerializersKt(Serializers.kt:99)
	at kotlinx.serialization.SerializersKt__SerializersKt.serializer(Serializers.kt:59)
	at kotlinx.serialization.SerializersKt.serializer(Unknown Source)
	at MainKt.main(Main.kt:98)
How does one convert a Map<String, Any?> to data class? Thanks in advance!
c
You cannot deserialize
Any?
because it's a security risk (it can be used by an attacker to execute arbitrary code on your server).
1
Why are you using a map anyway? If you just want to convert between maps and data classes, there are libraries that are much more suited for this than Serialization (e.g. #arrow optics)
arrow intensifies 2
If your primary representation is the map, you could even use delegation to have typesafe accessors that directly operate on the map: e.g.
Copy code
data class Person(val data: Map<String, Any?>) {
    val uuid: String by data
    val age: Int by data
    val name: String by data
    val address: Address by data
}
(of course this has the usual downsides of using a map as storage, including a bit slower access due to hashes and casts everywhere)
a
@CLOVIS Thanks for the suggestions! I’ve considered delegations - but they don’t work in my use case. I might have to look further into arrow.
c
Arrow is a collection of libraries for multiple goals, Arrow Optics specifically makes conversions between in-memory objects easier For example they provide the conversion between all 2-arguments data classes and
Pair
. I don't know if they provide conversions for
Map
by default
I'm curious why you're using maps at all, though, where do they come from?
a
You cannot deserialize
Any?
I thought you could, so long as you registered `Any` as polymorphic? (although I don’t think that helps in this instance)
c
@Adam S you can create your own serializer and register each subclass one by one, yes
You'll have to give a way to KotlinX to know which specific class it is in that case though, because it cannot figure out which subclass is actually present by itself
a
I’m curious why you’re using maps at all, though, where do they come from?
It’s for dynamodb reads and writes. I don’t want to use the expensive bean based reads & writes. Let’s say
Any
will only be one of the following types - String, Int, Long, Double, and Boolean. Would it be possible by tweaking the original code?
c
It would be possible, yes, but honestly it would be easier to use Arrow Optics or just write your own conversion function
a
yeah - then it’s super easy. Just iterate over the map entries and convert to a JsonObject using
jsonObjectBuilder {}
c
From your example though,
Any
can be
Map<String, Any?>
(
address.city
)
Why not just use
JsonObject
instead of
Map
? It already has all the types you care about
And KotlinX can serialize/deserialize to that if you really want to generate everything.
It's also probably much easier to write
Map<String, Any?> <=> JsonObject
conversion functions
a
I actually like this approach. Lemme try it out. Thanks @CLOVIS and @Adam S
c
If you have further questions, you'll probably have faster answers in #serialization
cheers 1
a
here’s an example that will recursively convert a
Map<String, Any?>
to a JsonObject
🔥 1
c
I would at least add a warning log when the type is not recognized instead of silently ignoring it
💯 2
a
This worked like a charm
👍 1
c
I hadn't thought of exploiting the code generator to convert to/from maps 🤔 maybe this could be added as a new format to the library
a
Do you mean the arrow optics lib?