KMapper
2.2.2indexedCompile-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, 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:
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:
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:
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
.
3.0.0 is a breaking change: generated mappers now return the plain by default
(it throws on a hard failure) instead of — see
to opt back into ,
, or your own wrapper. Upgrading from 2.x, see the section of that
same page; upgrading from 1.x?
·
.
Design ledger & implementation plans live in docs/superpowers/ and
docs/converter-redesign.md.
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.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.| 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 |
toX()toXResult(): Result<X>ResultFlowSurfaced from shared tags and platforms — no rankings paid for.