
Compile-time generation of type-safe extension mappers between data classes, with automatic property matching, renaming, nested and collection mapping, enum conversion, custom converters, and reverse mappers.
Every Kotlin codebase has that file. Forty lines of id = id, name = name, email = email that nobody wanted to write and nobody wants to review. Kraft deletes it:
- fun User.toUserDto(): UserDto = UserDto(
- id = id,
- name = name,
- email = email,
- )
+ @MapConfig(source = User::class, target = UserDto::class)
+ object UserMapperval dto = user.toUserDto() // generated at compile timeWhen the models differ, add one line per rule, directly on the object:
data class UserDto(val name: String, val mail: String, val internalNotes: String)
data class User(val fullName: String, val email: String, val currency: String, val internalNotes: String = "")
@MapConfig(source = UserDto::class, target = User::class)
@FieldMapping(source = "name", target = "fullName")
@FieldMapping(source = "mail", target = "email")
@MapIgnoreField("internalNotes")
object UserMapper {
@MapConstant val currency = "HUF" // generated: currency = UserMapper.currency
}That's the whole mapper. Kraft is a KSP processor that generates type-safe extension functions between your data classes — plain, readable Kotlin you can inspect in build/generated. No reflection, no runtime library, nothing to ship.
null around at runtime.commonMain; JVM, Android, iOS, JS, and WasmJs all work.@MapField, or flat @FieldMapping lines on the @MapConfig object, for cross-name mappingList<T> and Set<T> with nested element mappingUserId(val value: String) are wrapped and unwrapped automatically, including nullable and collection properties@MapEnum with auto-matching and custom entry pairs@MapReverse generates the inverse mapper automatically@MapUsing for property-source or whole-source transformations@KraftConverter on a top-level extension is auto-discovered across the module and the classpath, with @OptIn markers propagated onto the generated mapper@MapIgnore and @MapIgnoreField with directional control@MapConstant fills a target property from a member of the @MapConfig object@MapConfig for mapping without modifying data classesDto / Domain / Entity sides via the kraft { } Gradle DSL to emit short .toDomain() / .toEntity() extensions alongside the verbose mappersMapperGeneratorProvider to plug in your own code generatorKraft runs entirely at build time; the generated mappers are plain Kotlin and the annotations target JVM 1.8, so legacy apps on older runtimes are supported — see Compatibility and legacy projects.
Kraft is published to Maven Central under the com.blu3berry.kraft group (latest version shown in the badge above). Make sure mavenCentral() is in your repositories { } block.
The Gradle plugin is the one-line setup for Kotlin Multiplatform, JVM, and Android modules — it adds the version-aligned dependencies and all wiring:
// build.gradle.kts
plugins {
kotlin("multiplatform") // or kotlin("jvm") / kotlin("android")
id("com.google.devtools.ksp") version "<ksp-version>"
id("com.blu3berry.kraft") version "<version>"
}Optional configuration goes through the typed kraft { } extension:
kraft {
side("dto") { packagePattern = "com.example.dto.**" } // emits .toDto() aliases
side("domain") { packagePattern = "com.example.domain.**" } // emits .toDomain() aliases
}Prefer explicit control, or need the legacy-project integration patterns? See Getting Started for manual wiring.
Full guides for every feature live on the docs site:
kraft { } DSL, side aliases)| Annotation | Purpose | Placement |
|---|---|---|
@MapConfig |
Standalone mapping config | On object |
@MapFrom |
Map from source class | On target data class |
@MapTo |
Map to target class | On source data class |
@MapField |
Rename a property | On property in @MapFrom/@MapTo class |
@MapNested |
|
On property in @MapFrom/@MapTo class |
@MapIgnore |
Skip a property | On property (must have default value) |
@MapUsing |
Custom converter function | On function in @MapConfig object |
@KraftConverter |
Globally discoverable converter | On top-level extension function |
@MapEnum |
Enum-to-enum mapping | On object |
@MapReverse |
Generate inverse mapper | On class or @MapConfig object |
aliasEmitMode |
Per-mapper alias control (AliasEmitMode enum) |
Parameter on @MapConfig and @MapEnum
|
ConverterDirection |
Direction selector for @MapUsing (AUTO / FORWARD / REVERSE) |
Parameter on @MapUsing(direction = …)
|
IgnoreSide |
Direction selector for @MapIgnoreField (TARGET / SOURCE / BOTH) |
Parameter on @MapIgnoreField(direction = …)
|
@FieldMapping |
Config-level rename (repeatable) | On the @MapConfig object, or in @MapConfig.fieldMappings
|
@MapConstant |
Fixed target value (val once, fun per call) |
On a property or function inside the @MapConfig object |
@NestedMapping |
|
In @MapConfig.nestedMappings
|
@MapIgnoreField |
Config-level ignore (repeatable) | On the @MapConfig object, or in @MapConfig.ignoredMappings
|
Copyright Kraft Contributors
Licensed under the Apache License, Version 2.0
See LICENSE for the full text.
Every Kotlin codebase has that file. Forty lines of id = id, name = name, email = email that nobody wanted to write and nobody wants to review. Kraft deletes it:
- fun User.toUserDto(): UserDto = UserDto(
- id = id,
- name = name,
- email = email,
- )
+ @MapConfig(source = User::class, target = UserDto::class)
+ object UserMapperval dto = user.toUserDto() // generated at compile timeWhen the models differ, add one line per rule, directly on the object:
data class UserDto(val name: String, val mail: String, val internalNotes: String)
data class User(val fullName: String, val email: String, val currency: String, val internalNotes: String = "")
@MapConfig(source = UserDto::class, target = User::class)
@FieldMapping(source = "name", target = "fullName")
@FieldMapping(source = "mail", target = "email")
@MapIgnoreField("internalNotes")
object UserMapper {
@MapConstant val currency = "HUF" // generated: currency = UserMapper.currency
}That's the whole mapper. Kraft is a KSP processor that generates type-safe extension functions between your data classes — plain, readable Kotlin you can inspect in build/generated. No reflection, no runtime library, nothing to ship.
null around at runtime.commonMain; JVM, Android, iOS, JS, and WasmJs all work.@MapField, or flat @FieldMapping lines on the @MapConfig object, for cross-name mappingList<T> and Set<T> with nested element mappingUserId(val value: String) are wrapped and unwrapped automatically, including nullable and collection properties@MapEnum with auto-matching and custom entry pairs@MapReverse generates the inverse mapper automatically@MapUsing for property-source or whole-source transformations@KraftConverter on a top-level extension is auto-discovered across the module and the classpath, with @OptIn markers propagated onto the generated mapper@MapIgnore and @MapIgnoreField with directional control@MapConstant fills a target property from a member of the @MapConfig object@MapConfig for mapping without modifying data classesDto / Domain / Entity sides via the kraft { } Gradle DSL to emit short .toDomain() / .toEntity() extensions alongside the verbose mappersMapperGeneratorProvider to plug in your own code generatorKraft runs entirely at build time; the generated mappers are plain Kotlin and the annotations target JVM 1.8, so legacy apps on older runtimes are supported — see Compatibility and legacy projects.
Kraft is published to Maven Central under the com.blu3berry.kraft group (latest version shown in the badge above). Make sure mavenCentral() is in your repositories { } block.
The Gradle plugin is the one-line setup for Kotlin Multiplatform, JVM, and Android modules — it adds the version-aligned dependencies and all wiring:
// build.gradle.kts
plugins {
kotlin("multiplatform") // or kotlin("jvm") / kotlin("android")
id("com.google.devtools.ksp") version "<ksp-version>"
id("com.blu3berry.kraft") version "<version>"
}Optional configuration goes through the typed kraft { } extension:
kraft {
side("dto") { packagePattern = "com.example.dto.**" } // emits .toDto() aliases
side("domain") { packagePattern = "com.example.domain.**" } // emits .toDomain() aliases
}Prefer explicit control, or need the legacy-project integration patterns? See Getting Started for manual wiring.
Full guides for every feature live on the docs site:
kraft { } DSL, side aliases)| Annotation | Purpose | Placement |
|---|---|---|
@MapConfig |
Standalone mapping config | On object |
@MapFrom |
Map from source class | On target data class |
@MapTo |
Map to target class | On source data class |
@MapField |
Rename a property | On property in @MapFrom/@MapTo class |
@MapNested |
|
On property in @MapFrom/@MapTo class |
@MapIgnore |
Skip a property | On property (must have default value) |
@MapUsing |
Custom converter function | On function in @MapConfig object |
@KraftConverter |
Globally discoverable converter | On top-level extension function |
@MapEnum |
Enum-to-enum mapping | On object |
@MapReverse |
Generate inverse mapper | On class or @MapConfig object |
aliasEmitMode |
Per-mapper alias control (AliasEmitMode enum) |
Parameter on @MapConfig and @MapEnum
|
ConverterDirection |
Direction selector for @MapUsing (AUTO / FORWARD / REVERSE) |
Parameter on @MapUsing(direction = …)
|
IgnoreSide |
Direction selector for @MapIgnoreField (TARGET / SOURCE / BOTH) |
Parameter on @MapIgnoreField(direction = …)
|
@FieldMapping |
Config-level rename (repeatable) | On the @MapConfig object, or in @MapConfig.fieldMappings
|
@MapConstant |
Fixed target value (val once, fun per call) |
On a property or function inside the @MapConfig object |
@NestedMapping |
|
In @MapConfig.nestedMappings
|
@MapIgnoreField |
Config-level ignore (repeatable) | On the @MapConfig object, or in @MapConfig.ignoredMappings
|
Copyright Kraft Contributors
Licensed under the Apache License, Version 2.0
See LICENSE for the full text.