Kotlin / kotlinx-rpc

Add asynchronous RPC services to your multiplatform applications.
https://kotlin.github.io/kotlinx-rpc/
Apache License 2.0
514 stars 7 forks source link

Ability to specify custom serializers in Services #89

Open Neitex opened 3 weeks ago

Neitex commented 3 weeks ago

Hi! I'm currently trying to implement RPC for database access for an admin panel of my pet-project. Server-side database API utilizes Java's LocalDate and LocalDateTime classes, which do not have default serializers in kotlinx.serialization, but I've written custom serializers for them by hand.

I've tried to annotate all LocalDate fields with @Serializable() annotation specifying serializer to use, but that didn't help and build still failed with messages like: DatabaseAbsenceRPCClient.kt:58:58 Serializer has not been found for type 'LocalDate'. To use context serializer as fallback, explicitly annotate type or property with @Contextual. Not including these annotations doesn't affect build's failure to build too. :)

Declaration file:

interface DatabaseAbsenceRPC : RPC {
 // ...
 suspend fun getAbsences(date: @Serializable(with = LocalDateSerializer::class) LocalDate): List<AbsenceRecord>
}

So, my question is: would it be possible to somehow read these annotations in code-generation time and apply them to the generated code too? I think, I'm not the only one who uses custom serializers in kotlinx.serialization, so that would be a nice-to-have feature so we don't have to write boilerplate code to transform from-LocalDate-to-String and back :)

Awesome work on the library, btw! Looks promising!

Mr3zee commented 3 weeks ago

Hi! Thank for the kind words and your question!

Indeed, right now @Serializable(with = ...) is not supported. Looking at it, I'm not sure this the way to support it, but the use case is valid still. For the current version, you may use this workaround:

// todo update serializer to work with the value class
@Serializable(with = LocalDateSerializer::class) 
@JvmInline
value class LocalDateValue(val value: LocalDate)

interface DatabaseAbsenceRPC : RPC {
    suspend fun getAbsences(date: LocalDateValue): List<AbsenceRecord>
}

We actually support @Contextual annotation, so in general case you should be able to do this:

interface DatabaseAbsenceRPC : RPC {
    suspend fun getAbsences(@Contextual date: LocalDate): List<AbsenceRecord>
}

// and, for example, with kRPC protocol

rpcClientConfig {
    serialization {
        json { // for example, json
            serializersModule = SerializersModule {
                contextual(LocalDate::class) {
                    // do the thing
                }
            }
        }
    }
}

But our current codegen does not copy the parameter annotation, with is a problem on our side. (So it actually works with custom classes as arguments with contextual properties inside them, but not with the method arguments themselves) I will look into how and which annotations we should support. The case with @Contextual will definitely be fixed, and maybe we will add some others.

martin-ncs commented 3 days ago

Hi, we are currently implementing RPC and we'd appreciate having @Contextual support, any idea when it will released?

Mr3zee commented 3 days ago

Hi! Probably with or right after K2 support in 0.2.1