Conceptual Question: I want to deserialize a JsonP...
# serialization
f
Conceptual Question: I want to deserialize a JsonPrimitive (e.g. an integer) into specific class that is not an Integer because in the context of my code it is more than simply an Integer (e.g. a user ID that can be mapped to a User object using some DB). Is there some elegant way to do this with kotlinx.serialization? Because this requires more external information, this would probably need me to specify the mapping from
Integer -> User
when I instantiate the JSON deserializer. Is there such a functionality already available?
https://github.com/Kotlin/kotlinx.serialization/blob/master/docs/serializers.md#contextual-serialization seems like what I want, but this still takes the class as an argument and doesn't allow me to pass the factory method to create the User object from an Integer. I guess I could provide an anonymous class instead of
DateAsLongSerializer
that captures the required information for the
Integer -> User
conversion
Copy code
private val module = SerializersModule { 
    contextual(DateAsLongSerializer)
}
d
Why can't you hard code the factory into the serialiser?
f
What do you mean by hardcode?
My rough use case is:
Copy code
fun importData(database: UserDB, data: String) {
    return Json.decodeFromString(SomeClassContainingUserFields.serializer(), data)
}
Maybe we mean the same thing by "hardcoding" and "provide an anonymous class as a Serializer that captures the required information for the 
Integer -> User
 conversion"
d
Maybe
I'd have to see code. Too tired to speak in vague.
f
Okay, I got it working with an anonymous object:
Copy code
val DB = ...
val module = SerializersModule {
                contextual(object: KSerializer<User> {
                    override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("User", PrimitiveKind.LONG)
                    override fun serialize(encoder: Encoder, value: User) = encoder.encodeLong(value.id)
                    override fun deserialize(decoder: Decoder): User = DB.getUser(decoder.decodeLong())
                })
            }
val json = Json {
    serializersModule = module
}
d
Nice, I understand the problem now. I think that's the way to do it.
f
Thanks for the feedback! Now that I understand what I needed to do, this is quite elegant and really useful for my scenario :)
Somewhat of a followup question on good program design: I have lots of fields in my JSON that could be deserialized in this way, i.e. the transformation from some primitive to a application specific object could be crammed into the deserializer. Is it a bad sign if my dataclasses end up consisting of mostly
@Contextual
fields and deserializing requires 5-10 contextual Serializers for various types?
d
Hmm, it is indeed a bit weird but I don't think it's bad.
f
What I am really doing is loading fairly specific analysis results from a Python project, into an independent JVM project. And I am not sure if the cleaner way would be to deserialize the JSON into a dataclass that only has the primitives as they were exported and separate the logic that transforms this into the context of the other application
d
The use of DB is the only thing I'm suspicious of but I don't see a way around it. 🤷🏼
f
It's kinda hard to explain without using highly domain specific knowledge/terminology
One simple example is that some values represent addresses in a process memory space. So e.g. that a certain piece of data is at address
0x1000
in memory. The python application exports this is
addr: 0x1000
in the JSON (and uses a python variable with an int value internally), but the application I am importing this into has a dedicated
Address
object.
But some fields represent the address of a function in memory. They are also exported as an integer, e.g.
func_addr: 0x2000
and those could be serialized into the dedicated
Function
object the application has.
I guess overall it won't be that much code and I can refactor it if whatever way I choose ever becomes an issue.
d
Yeah
f
Mmh. Is it possible to add a field in a dataclass that isn't in the underlying JSON? e.g. from the JSON
Copy code
{ user_id: 42 }
to the dataclass
Copy code
data class User(val user_id: Int, val user: User)
d
In general yes but it'll need a default value or a custom serialiser.