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: Result<User> = UserResponse(7, "2026-06-12").toUserResult()
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, 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:2.2.2")
implementation("io.github.sahsenvar:kmapper-annotations:2.2.2")
ksp("io.github.sahsenvar:kmapper-compiler:2.2.2")
}
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: 2.2.2 (11 artifacts) — on
Maven Central.
2.x is the converter-subsystem redesign; upgrading from 1.x?
Migration guide ·
CHANGELOG.
Design ledger & implementation plans live in docs/superpowers/ and
docs/converter-redesign.md.
Result<T> — malformed wire data is a
typed MappingException, never a surprise crash. The fallback ladder
(value > constructor default > null > error) keeps one bad field from destroying a
payload, and every absorbed error is reported to an observability sink.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 → toXResult() extensions) |
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 |
Surfaced from shared tags and platforms — no rankings paid for.