
Compile-time, type-safe object mapping via annotations; generated mappers return Result<T> with path-aware errors, compile-time lossless-conversion checks, fallback ladder, pluggable converters and observability.
Compile-time object mapping for Kotlin Multiplatform — annotate your wire models, get type-safe mappers generated by KSP. No reflection, no runtime registry, no boilerplate.
data class User(val id: Long, val joined: LocalDate)
@MapTo(User::class)
data class UserResponse(val id: Long, val joined: String)
// generated at compile time:
val user: User = UserResponse(7, "2026-06-12").toUser()What makes that different from every mapper you've hand-written:
toX() throws a typed MappingException on a hard
failure — never a surprise crash from an unrelated bug. The fallback ladder
(value > constructor default > null > error) keeps one bad field from destroying a
payload, every absorbed error is reported to an observability sink, and if you'd rather
have failures as values, opt in per-mapping or module-wide with a
return wrapper (toXResult(): Result<X>,
toXFlow(): Flow<X>, or your own).Cannot convert customer.address.zipCode: … — R8-safe, three
objects deep.Long → Int fails the build with a guiding message
instead of silently truncating in production.Full guide on GitBook: https://kmapper.gitbook.io/docs
Source Markdown in the repo: English · Türkçe — installation, the mental model, field mapping, null-safety, converters, validation, collections, enums, return wrappers, error handling, observability, multi-module setup, full annotation reference.
Prefer reading code? The sample gallery has 26 runnable examples
covering every feature, ordered basic → advanced (./gradlew sample:runSample).
Working on a project that uses KMapper? Start here:
kmapper.gitbook.io/docs/llms.txt.)@Ignore, @UseMapTypeConverter, @ValidateFrom/To,
@MapDefaultValue)? Those names still resolve but fail compilation with guided
ERROR-level deprecations naming the 2.0 replacement — follow the message or the
migration guide.plugins {
id("com.google.devtools.ksp") version "2.3.10-2.0.5"
}
dependencies {
implementation("io.github.sahsenvar:kmapper-core:3.0.0")
implementation("io.github.sahsenvar:kmapper-annotations:3.0.0")
ksp("io.github.sahsenvar:kmapper-compiler:3.0.0")
}Want a module-wide return wrapper (e.g. every
mapping also gets toXResult()) without repeating wrapper = … on each @MapTo? Add the
optional Gradle plugin:
plugins {
id("io.github.sahsenvar.kmapper") version "3.0.0"
}
import com.sahsenvar.kmapper.gradle.KMapperWrapper
KMapper { wrapper = KMapperWrapper.KtResult }KMP setup and add-ons: installation guide.
Group io.github.sahsenvar:
| Artifact | Platform | Purpose |
|---|---|---|
kmapper-core |
KMP | Standalone runtime: MappingException, converter base + 35 built-in pairs, validators, conversion seams, KMapper/MappingListener — usable without code generation |
kmapper-annotations |
KMP | Mapping declaration annotations (@MapTo/@MapFrom/@FieldMap/@ConvertWith/@Validate/…) |
kmapper-compiler |
JVM | KSP code generator (@MapTo/@MapFrom → toX(), plus wrapper extensions like toXResult()) |
kmapper-gradle-plugin |
Gradle | Optional KMapper { wrapper = … } extension — sets the module-wide return wrapper without repeating wrapper = … on every annotation |
kmapper-converters-immutable |
KMP |
PersistentList/ImmutableList/PersistentSet/ImmutableSet wrappers |
kmapper-converters-arrow |
KMP |
NonEmptyList/NonEmptySet wrappers, Option<T> mapping |
kmapper-converters-datetime |
JVM/Android |
java.time converters (Instant, LocalDate, Duration, …) + kotlinx ↔ java bridges |
kmapper-converters-bignumber |
KMP (ionspin) / JVM+Android (java.math) |
String/Double/Long/Int ↔ BigDecimal/BigInteger; lossy directions refused at compile time |
kmapper-converters-uuid |
KMP / JVM+Android |
String ↔ kotlin.uuid.Uuid / java.util.UUID
|
kmapper-converters-okio |
KMP |
ByteString (UTF-8/Base64/Base64Url/Hex), Path
|
kmapper-converters-uri |
JVM / Android / iOS |
String ↔ java.net.URI / android.net.Uri / NSURL
|
kmapper-validators |
KMP | Email, URL, E.164 phone, IPv4/IPv6, hostname, UUID, slug, Base64/hex, geo, port, Luhn — for @Validate
|
kotlinx-datetime (LocalDate, Instant, …) and kotlin.time.Duration converters are
core built-ins — no add-on needed.
Latest release: 3.0.0 (12 artifacts) — on
Maven Central.
3.0.0 is a breaking change: generated mappers now return the plain toX() by default
(it throws on a hard failure) instead of toXResult(): Result<X> — see
return wrappers to opt back into Result,
Flow, or your own wrapper. Upgrading from 2.x, see the Migrating from 2.x section of that
same page; upgrading from 1.x?
Migration guide ·
CHANGELOG.
Design ledger & implementation plans live in docs/superpowers/ and
docs/converter-redesign.md.
Compile-time object mapping for Kotlin Multiplatform — annotate your wire models, get type-safe mappers generated by KSP. No reflection, no runtime registry, no boilerplate.
data class User(val id: Long, val joined: LocalDate)
@MapTo(User::class)
data class UserResponse(val id: Long, val joined: String)
// generated at compile time:
val user: User = UserResponse(7, "2026-06-12").toUser()What makes that different from every mapper you've hand-written:
toX() throws a typed MappingException on a hard
failure — never a surprise crash from an unrelated bug. The fallback ladder
(value > constructor default > null > error) keeps one bad field from destroying a
payload, every absorbed error is reported to an observability sink, and if you'd rather
have failures as values, opt in per-mapping or module-wide with a
return wrapper (toXResult(): Result<X>,
toXFlow(): Flow<X>, or your own).Cannot convert customer.address.zipCode: … — R8-safe, three
objects deep.Long → Int fails the build with a guiding message
instead of silently truncating in production.Full guide on GitBook: https://kmapper.gitbook.io/docs
Source Markdown in the repo: English · Türkçe — installation, the mental model, field mapping, null-safety, converters, validation, collections, enums, return wrappers, error handling, observability, multi-module setup, full annotation reference.
Prefer reading code? The sample gallery has 26 runnable examples
covering every feature, ordered basic → advanced (./gradlew sample:runSample).
Working on a project that uses KMapper? Start here:
kmapper.gitbook.io/docs/llms.txt.)@Ignore, @UseMapTypeConverter, @ValidateFrom/To,
@MapDefaultValue)? Those names still resolve but fail compilation with guided
ERROR-level deprecations naming the 2.0 replacement — follow the message or the
migration guide.plugins {
id("com.google.devtools.ksp") version "2.3.10-2.0.5"
}
dependencies {
implementation("io.github.sahsenvar:kmapper-core:3.0.0")
implementation("io.github.sahsenvar:kmapper-annotations:3.0.0")
ksp("io.github.sahsenvar:kmapper-compiler:3.0.0")
}Want a module-wide return wrapper (e.g. every
mapping also gets toXResult()) without repeating wrapper = … on each @MapTo? Add the
optional Gradle plugin:
plugins {
id("io.github.sahsenvar.kmapper") version "3.0.0"
}
import com.sahsenvar.kmapper.gradle.KMapperWrapper
KMapper { wrapper = KMapperWrapper.KtResult }KMP setup and add-ons: installation guide.
Group io.github.sahsenvar:
| Artifact | Platform | Purpose |
|---|---|---|
kmapper-core |
KMP | Standalone runtime: MappingException, converter base + 35 built-in pairs, validators, conversion seams, KMapper/MappingListener — usable without code generation |
kmapper-annotations |
KMP | Mapping declaration annotations (@MapTo/@MapFrom/@FieldMap/@ConvertWith/@Validate/…) |
kmapper-compiler |
JVM | KSP code generator (@MapTo/@MapFrom → toX(), plus wrapper extensions like toXResult()) |
kmapper-gradle-plugin |
Gradle | Optional KMapper { wrapper = … } extension — sets the module-wide return wrapper without repeating wrapper = … on every annotation |
kmapper-converters-immutable |
KMP |
PersistentList/ImmutableList/PersistentSet/ImmutableSet wrappers |
kmapper-converters-arrow |
KMP |
NonEmptyList/NonEmptySet wrappers, Option<T> mapping |
kmapper-converters-datetime |
JVM/Android |
java.time converters (Instant, LocalDate, Duration, …) + kotlinx ↔ java bridges |
kmapper-converters-bignumber |
KMP (ionspin) / JVM+Android (java.math) |
String/Double/Long/Int ↔ BigDecimal/BigInteger; lossy directions refused at compile time |
kmapper-converters-uuid |
KMP / JVM+Android |
String ↔ kotlin.uuid.Uuid / java.util.UUID
|
kmapper-converters-okio |
KMP |
ByteString (UTF-8/Base64/Base64Url/Hex), Path
|
kmapper-converters-uri |
JVM / Android / iOS |
String ↔ java.net.URI / android.net.Uri / NSURL
|
kmapper-validators |
KMP | Email, URL, E.164 phone, IPv4/IPv6, hostname, UUID, slug, Base64/hex, geo, port, Luhn — for @Validate
|
kotlinx-datetime (LocalDate, Instant, …) and kotlin.time.Duration converters are
core built-ins — no add-on needed.
Latest release: 3.0.0 (12 artifacts) — on
Maven Central.
3.0.0 is a breaking change: generated mappers now return the plain toX() by default
(it throws on a hard failure) instead of toXResult(): Result<X> — see
return wrappers to opt back into Result,
Flow, or your own wrapper. Upgrading from 2.x, see the Migrating from 2.x section of that
same page; upgrading from 1.x?
Migration guide ·
CHANGELOG.
Design ledger & implementation plans live in docs/superpowers/ and
docs/converter-redesign.md.