Florian Magin

    Florian Magin

    8 months ago
    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
    private val module = SerializersModule { 
        contextual(DateAsLongSerializer)
    }
    Dominaezzz

    Dominaezzz

    8 months ago
    Why can't you hard code the factory into the serialiser?
    Florian Magin

    Florian Magin

    8 months ago
    What do you mean by hardcode?
    My rough use case is:
    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"
    Dominaezzz

    Dominaezzz

    8 months ago
    Maybe
    I'd have to see code. Too tired to speak in vague.
    Florian Magin

    Florian Magin

    8 months ago
    Okay, I got it working with an anonymous object:
    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
    }
    Dominaezzz

    Dominaezzz

    8 months ago
    Nice, I understand the problem now. I think that's the way to do it.
    Florian Magin

    Florian Magin

    8 months ago
    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?
    Dominaezzz

    Dominaezzz

    8 months ago
    Hmm, it is indeed a bit weird but I don't think it's bad.
    Florian Magin

    Florian Magin

    8 months ago
    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
    Dominaezzz

    Dominaezzz

    8 months ago
    The use of DB is the only thing I'm suspicious of but I don't see a way around it. 🤷🏼
    Florian Magin

    Florian Magin

    8 months ago
    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.
    Dominaezzz

    Dominaezzz

    8 months ago
    Yeah
    Florian Magin

    Florian Magin

    8 months ago
    Mmh. Is it possible to add a field in a dataclass that isn't in the underlying JSON? e.g. from the JSON
    { user_id: 42 }
    to the dataclass
    data class User(val user_id: Int, val user: User)
    Dominaezzz

    Dominaezzz

    8 months ago
    In general yes but it'll need a default value or a custom serialiser.