Konstruct is a powerful Kotlin Symbol Processor (KSP) designed to automatically generate kotlinx.serialization serializers and deserializers using the Surrogate Pattern.
Serialization is easy when you control the source code. But what if you need to serialize:
- Third-party classes that aren't annotated with
@Serializable? - classes where you want to separate your domain model from its wire representation?
- classes that require complex custom mapping logic?
Konstruct solves this by generating surrogate data classes and mapped serializers automatically, saving you from writing thousands of lines of boilerplate code.
Add the dependency to your build.gradle.kts:
repositories {
mavenCentral()
}
dependencies {
// Both are required for the single artifact
implementation("dev.gallon:konstruct:1.0.0")
ksp("dev.gallon:konstruct:1.0.0")
}You can see a working example at izivia/ocpi-toolkit.
Annotate any class (typically a configuration class) with @GenerateSerializers to trigger the generation:
import dev.gallon.konstruct.annotations.GenerateSerializers
@GenerateSerializers(
classes = [
User::class,
Product::class
]
)
class MyAppSerializersKonstruct will generate:
UserSurrogate&ProductSurrogateUserSerializer&ProductSerializerGeneratedSerializersModule
import dev.gallon.konstruct.generated.generatedSerializersModule
import kotlinx.serialization.json.Json
val json = Json {
serializersModule = generatedSerializersModule
}
// Now you can serialize/deserialize User even if it's not @Serializable
val user = User(id = 1, name = "John Doe")
val encoded = json.encodeToString(UserSerializer, user)
val decoded = json.decodeFromString(UserSerializer, encoded)You can override defaults at the class or field level:
@GenerateSerializers(
classes = [MyClass::class],
customClassSerializers = [
CustomClassSerializer(targetClass = Instant::class, serializer = MyInstantSerializer::class)
],
customFieldSerializers = [
CustomFieldSerializer(
targetClass = MyClass::class,
fieldSerializer = [
FieldSerializer(name = "secretField", serializer = EncryptedSerializer::class)
]
)
]
)
class MySerializersKonstruct uses the Surrogate Pattern. Instead of serializing your domain class directly, it serializes a "shadow" data class (the surrogate) that is annotated with @Serializable.
graph LR
A[Domain Class] <--> B[Mapped Serializer]
B <--> C[Surrogate Class]
C <--> D[JSON / Binary]
This ensures that your domain model remains pure and untouched by serialization details, while still being fully serializable.
Contributions are welcome! Whether it's reporting a bug, suggesting a feature, or submitting a pull request.
- Fork the repository.
- Create your feature branch (
git checkout -b feature/amazing-feature). - Commit your changes (
git commit -m 'Add some amazing feature'). - Push to the branch (
git push origin feature/amazing-feature). - Open a Pull Request.
This project was originally built for IZIVIA OCPI Toolkit.
- Lilian Gallon (@lilgallon) - Lead Developer
This project is licensed under the MIT License - see the LICENSE file for details.