Lightweight annotation-based library for generating type-safe mappers in Kotlin Multiplatform using KSP 2. The
komap-annotations module is KMP-ready, while code generation is performed via KSP in commonMain (metadata) using the
JVM processor komap-processor.
- KMP-first: generate from
commonMainfor JVM/JS/Native, no platform-specific APIs - Bidirectional mapping via
@Komap(from = [...], to = [...]) - Property renaming:
@MapName("sourceName") - Ignore parameters:
@IgnoreInMapping - Custom converters:
@KomapProvider(qualifier = ...)+@MapQualifier("...") - Construction via factories:
@KomapFactory(qualifier = ...)with selection throughfactoryQualifiers - Collections and defaults: overloads for
Iterable; missing values are exposed as parameters/lambdas
Prereqs: Kotlin 2.2.0, KSP 2.2.0-2.0.2, repository mavenCentral().
// settings.gradle.kts
pluginManagement {
repositories { google(); gradlePluginPortal(); mavenCentral() }
}// build.gradle.kts (module with your models)
plugins {
kotlin("multiplatform")
id("com.google.devtools.ksp")
}
kotlin {
jvm()
js { nodejs() }
// add other targets as needed
sourceSets {
val commonMain by getting {
// Include KSP generated sources
kotlin.srcDir("build/generated/ksp/metadata/commonMain/kotlin")
dependencies {
implementation("ua.wwind.komap:komap-annotations:1.0.0")
}
}
}
}
dependencies {
// Wire the KSP processor for common metadata
add("kspCommonMainMetadata", "ua.wwind.komap:komap-processor:1.0.0")
}Generate code:
./gradlew kspCommonMainKotlinMetadataGenerated sources live in build/generated/ksp/metadata/commonMain/kotlin.
// API model
data class ApiUser(
val id: String,
val name: String,
val role: String,
)
// Domain model with annotations
@Komap(to = [ApiUser::class])
data class User(
@MapName(name = "id") val userId: String,
val name: String,
@IgnoreInMapping val role: String, // not read from source; becomes a mapper parameter
)
// After generation you get:
// fun User.toApiUser(role: String): ApiUser
// fun Iterable<User>.toApiUser(provideRole: (User) -> String): List<ApiUser>Usage:
val user = User(userId = "42", name = "Ada", role = "ADMIN")
val api = user.toApiUser(role = "USER")
val list = listOf(user).toApiUser { it.role }// Example: Email String
@KomapProvider(qualifier = "plain")
fun Email.toStringPlain(): String = this.value
@KomapProvider
fun String.toEmail(): Email = Email(this)
// Use the selected provider
data class Profile(
@MapQualifier("plain") val email: String,
)// Must be in the same module as the target class
@KomapFactory(qualifier = "fromName")
fun ApiUser(name: String, id: String, role: String): ApiUser = ApiUser(id, name, role)
@Komap(to = [ApiUser::class], factoryQualifiers = ["fromName"])
data class User(
@MapName("id") val userId: String,
val name: String,
@IgnoreInMapping val role: String,
)The processor selects a factory by matching return type and provided factoryQualifiers.
- Annotations (
komap-annotations): Android (Library), JVM, JS (Node), WASM JS, Linux X64/ARM64, macOS X64/ARM64, Windows (mingwX64), iOS X64/ARM64/Simulator - Processor (
komap-processor): JVM (via KSP in metadata for KMP)
See the komap-samples module:
basic/BasicSample.kt— basic mappings and collectionsfactory/FactorySample.kt— factories with qualifiersproviders/— examples of@KomapProvider
Run generation in the sample:
./gradlew :komap-samples:kspCommonMainKotlinMetadataApache License 2.0. See POM details and the repository page: White Wind LLC — komap.
PRs and discussions are welcome. Please keep the code style and add small examples to komap-samples for new features.