-
Notifications
You must be signed in to change notification settings - Fork 660
Description
Problem Statement: The Annotation Intrusiveness Dilemma
Currently, Kotlinx Serialization follows an intrusive approach where classes must be annotated with @serializable to enable serialization. This creates several ecosystem challenges:
The Real-world Pain Point
kotlin
// Library author's code - they don't care about serialization
data class ApiResponse(
val data: String,
val status: Int,
val metadata: Map<String, Any>
)
// Consumer wants to serialize this - BUT CAN'T!
// They're forced to use workarounds:
val response = ApiResponse("hello", 200, emptyMap())
// β This doesn't work - no @serializable annotation
// Json.encodeToString(response)
// β
Consumers must create wrapper classes - boilerplate heaven!
@serializable
data class SerializableApiResponse(
val data: String,
val status: Int,
val metadata: Map<String, Any>
) {
constructor(original: ApiResponse) : this(...)
}
Ecosystem Impact
Library Authors: Forced to add Kotlinx Serialization as a dependency and annotate their models
KMP Compatibility: Libraries aiming for minimal dependencies can't use serialization without forcing it on consumers
Legacy Code: Impossible to serialize classes from Java libraries or older Kotlin code
Annotation Pollution: Every data class gets @serializable whether it needs serialization or not
Proposed Solution: Consumer-side Declaration
Enable serialization declaration at the consumption site rather than the declaration site.
Option A: File-level Declaration
kotlin
// In consumer module - no library modification needed
@file:GenerateSerializersFor(
com.some.library.ApiResponse::class,
com.some.library.UserDTO::class
)
fun main() {
val response = ApiResponse("data", 200, mapOf())
val json = Json.encodeToString(response) // Just works! π
}
Option B: Configuration-based
kotlin
// serialization-config.kts
externalClasses {
serializable(ApiResponse::class)
serializable(UserDTO::class) {
rename("userId" to "user_id")
}
}
Option C: Extension-based
kotlin
// Declare serializability via extensions
val ApiResponse.serializer: KSerializer
@ExperimentalSerializationApi
get() = generatedSerializer()
Benefits for KMP/JVM Ecosystem
For Library Authors
Zero dependency: No need to depend on kotlinx-serialization
Clean API: Models remain framework-agnostic
Better KMP support: Minimal dependencies for multiplatform libraries
For Consumers
No wrappers: Direct serialization of any class
Flexibility: Choose serialization strategy per use case
Migration friendly: Gradually adopt serialization without refactoring
For Kotlin Ecosystem
Interop: Serialize Java classes, third-party library models
Progressive: Works alongside existing @serializable approach
Tooling: IDE support for external class configuration
Technical Considerations
Compiler Plugin Extension: Extend KCP to process consumer declarations
Symbol Resolution: Handle external classes from binaries/dependencies
Module System: Ensure generated serializers are properly registered
Incremental Compilation: Maintain build performance
Community Impact
This change would position Kotlinx Serialization as a more ecosystem-friendly solution, competing better with reflection-based alternatives like Jackson while maintaining compile-time safety.
Discussion Points
Is consumer-side declaration feasible with current compiler architecture?
What's the optimal syntax for declaring external serializers?
How to handle complex cases (custom serializers, polymorphic hierarchies)?
Should this be a separate plugin or integrated into the main one?