
Type-safe library enables automated data class mapping with compile-time validation, custom converters, seamless dependency injection integration, null safety, and an extensible architecture.
kMapper is a powerful, type-safe Kotlin Multiplatform library for automated data class mapping. It provides a simple and efficient way to map between different data classes while maintaining type safety and compile-time validation. It serves similar reason to https://mapstruct.org/ but provides native Kotlin support while MapStruct often struggles to do so.
build.gradle.kts:plugins {
id("com.google.devtools.ksp") version "2.2.10-2.0.2"
}
dependencies {
// Core dependencies
implementation("io.github.s0nicyouth:processor_annotations:1.3.0")
implementation("io.github.s0nicyouth:converters:1.3.0")
ksp("io.github.s0nicyouth:processor:1.3.0")
}build.gradle.kts:plugins {
id("com.google.devtools.ksp") version "2.2.0-2.0.2"
}
kotlin {
// ...
// KSP Common sourceSet
sourceSets.named("commonMain").configure {
kotlin.srcDir("build/generated/ksp/metadata/commonMain/kotlin")
}
}
dependencies {
// Core dependencies
implementation("io.github.s0nicyouth:processor_annotations:1.3.0")
implementation("io.github.s0nicyouth:converters:1.3.0")
with("io.github.s0nicyouth:processor:1.3.0") {
add("kspCommonMainMetadata", this)
add("kspAndroid", this)
add("kspIosX64", this)
add("kspIosArm64", this)
add("kspIosSimulatorArm64", this)
}
}
// KSP Metadata Trigger
project.tasks.withType(KotlinCompilationTask::class.java).configureEach {
if (name != "kspCommonMainKotlinMetadata") {
dependsOn("kspCommonMainKotlinMetadata")
}
}Or just simple use "kmapper-gradle-plugin"
plugins {
id("com.google.devtools.ksp") version "2.2.0-2.0.2"
id("io.github.s0nicyouth.kmapper-plugin")
}kmapper/
βββ examples/ # Example projects
β βββ anvil/ # Anvil integration example
β βββ koin/ # Koin integration example
β βββ media/ # Demo videos and screenshots
β βββ sampleApplication/ # Sample application
β βββ composeApp/ # Shared Compose Multiplatform code
β βββ iosApp/ # iOS application
βββ kmapper_plugin/ # Gradle plugin
βββ processor/ # KSP processor
βββ processor_annotations/ # Annotations for the processor
βββ converters/ # Built-in converters
data class UserDto(
val id: Long,
val name: String,
val email: String
)
data class User(
val id: Long,
val name: String,
val email: String
)@Mapper annotation:@Mapper
interface UserMapper {
fun toDto(user: User): UserDto
fun fromDto(dto: UserDto): User
}The implementation will be generated at compile time. You can then use it like this:
val user = User(1, "John Doe", "john@example.com")
val userDto = UserMapperImpl().toDto(user)For custom field mappings, you can use the @Mapping annotation:
data class UserDto(
val userId: Long,
val fullName: String,
val emailAddress: String
)
data class User(
val id: Long,
val name: String,
val email: String
)
@Mapper
interface UserMapper {
@Mapping(source = "userId", target = "id")
@Mapping(source = "fullName", target = "name")
@Mapping(source = "emailAddress", target = "email")
fun toDto(user: User): UserDto
@Mapping(source = "id", target = "userId")
@Mapping(source = "name", target = "fullName")
@Mapping(source = "email", target = "emailAddress")
fun fromDto(dto: UserDto): User
}// 1. Define your mapper interface
@Mapper
interface UserMapper {
fun toDto(user: User): UserDto
fun fromDto(dto: UserDto): User
}
// 2. Create a Koin module
val appModule = module {
factory { UserMapperImpl() }
factory { UserRepository(get()) }
}
// 3. Start Koin with your module
fun main() {
startKoin {
modules(appModule)
}
}
// or with Koin Annotations
fun main(args: Array<String>) {
startKoin {
modules(defaultModule)
}
}
// 4. Inject and use the mapper
class UserRepository (
private val userMapper: UserMapper
) {
fun getUser(id: Long): User {
val dto = api.getUser(id)
return userMapper.fromDto(dto)
}
}// 1. Define your mapper interface
@Mapper
interface UserMapper {
fun toDto(user: User): UserDto
fun fromDto(dto: UserDto): User
}
// 2. Define your scope
abstract class AppScope private constructor()
// 3. Create a Dagger module for mappers
@Module
@ContributesTo(AppScope::class)
object MappersModule {
@Provides
@SingleIn(AppScope::class)
fun provideUserMapper(): UserMapper = UserMapperImpl()
}
// 4. Create your component
@Singleton
@MergeComponent(AppScope::class)
interface AppComponent {
fun getUserMapper(): UserMapper
}
// 5. Use the mapper
fun main() {
val component = DaggerAppComponent.create()
val mapper = component.getUserMapper()
val user = User(1, "John Doe", "john@example.com")
val dto = mapper.toDto(user)
}Check out the demo videos in the examples/media directory:
sampleApplication run configurationexamples/sampleApplication/iosApp/iosApp.xcworkspace
./gradlew :examples:sampleApplication:composeApp:runCopyright 2023
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
Contributions are welcome! Please feel free to submit a Pull Request.
kMapper is a powerful, type-safe Kotlin Multiplatform library for automated data class mapping. It provides a simple and efficient way to map between different data classes while maintaining type safety and compile-time validation. It serves similar reason to https://mapstruct.org/ but provides native Kotlin support while MapStruct often struggles to do so.
build.gradle.kts:plugins {
id("com.google.devtools.ksp") version "2.2.10-2.0.2"
}
dependencies {
// Core dependencies
implementation("io.github.s0nicyouth:processor_annotations:1.3.0")
implementation("io.github.s0nicyouth:converters:1.3.0")
ksp("io.github.s0nicyouth:processor:1.3.0")
}build.gradle.kts:plugins {
id("com.google.devtools.ksp") version "2.2.0-2.0.2"
}
kotlin {
// ...
// KSP Common sourceSet
sourceSets.named("commonMain").configure {
kotlin.srcDir("build/generated/ksp/metadata/commonMain/kotlin")
}
}
dependencies {
// Core dependencies
implementation("io.github.s0nicyouth:processor_annotations:1.3.0")
implementation("io.github.s0nicyouth:converters:1.3.0")
with("io.github.s0nicyouth:processor:1.3.0") {
add("kspCommonMainMetadata", this)
add("kspAndroid", this)
add("kspIosX64", this)
add("kspIosArm64", this)
add("kspIosSimulatorArm64", this)
}
}
// KSP Metadata Trigger
project.tasks.withType(KotlinCompilationTask::class.java).configureEach {
if (name != "kspCommonMainKotlinMetadata") {
dependsOn("kspCommonMainKotlinMetadata")
}
}Or just simple use "kmapper-gradle-plugin"
plugins {
id("com.google.devtools.ksp") version "2.2.0-2.0.2"
id("io.github.s0nicyouth.kmapper-plugin")
}kmapper/
βββ examples/ # Example projects
β βββ anvil/ # Anvil integration example
β βββ koin/ # Koin integration example
β βββ media/ # Demo videos and screenshots
β βββ sampleApplication/ # Sample application
β βββ composeApp/ # Shared Compose Multiplatform code
β βββ iosApp/ # iOS application
βββ kmapper_plugin/ # Gradle plugin
βββ processor/ # KSP processor
βββ processor_annotations/ # Annotations for the processor
βββ converters/ # Built-in converters
data class UserDto(
val id: Long,
val name: String,
val email: String
)
data class User(
val id: Long,
val name: String,
val email: String
)@Mapper annotation:@Mapper
interface UserMapper {
fun toDto(user: User): UserDto
fun fromDto(dto: UserDto): User
}The implementation will be generated at compile time. You can then use it like this:
val user = User(1, "John Doe", "john@example.com")
val userDto = UserMapperImpl().toDto(user)For custom field mappings, you can use the @Mapping annotation:
data class UserDto(
val userId: Long,
val fullName: String,
val emailAddress: String
)
data class User(
val id: Long,
val name: String,
val email: String
)
@Mapper
interface UserMapper {
@Mapping(source = "userId", target = "id")
@Mapping(source = "fullName", target = "name")
@Mapping(source = "emailAddress", target = "email")
fun toDto(user: User): UserDto
@Mapping(source = "id", target = "userId")
@Mapping(source = "name", target = "fullName")
@Mapping(source = "email", target = "emailAddress")
fun fromDto(dto: UserDto): User
}// 1. Define your mapper interface
@Mapper
interface UserMapper {
fun toDto(user: User): UserDto
fun fromDto(dto: UserDto): User
}
// 2. Create a Koin module
val appModule = module {
factory { UserMapperImpl() }
factory { UserRepository(get()) }
}
// 3. Start Koin with your module
fun main() {
startKoin {
modules(appModule)
}
}
// or with Koin Annotations
fun main(args: Array<String>) {
startKoin {
modules(defaultModule)
}
}
// 4. Inject and use the mapper
class UserRepository (
private val userMapper: UserMapper
) {
fun getUser(id: Long): User {
val dto = api.getUser(id)
return userMapper.fromDto(dto)
}
}// 1. Define your mapper interface
@Mapper
interface UserMapper {
fun toDto(user: User): UserDto
fun fromDto(dto: UserDto): User
}
// 2. Define your scope
abstract class AppScope private constructor()
// 3. Create a Dagger module for mappers
@Module
@ContributesTo(AppScope::class)
object MappersModule {
@Provides
@SingleIn(AppScope::class)
fun provideUserMapper(): UserMapper = UserMapperImpl()
}
// 4. Create your component
@Singleton
@MergeComponent(AppScope::class)
interface AppComponent {
fun getUserMapper(): UserMapper
}
// 5. Use the mapper
fun main() {
val component = DaggerAppComponent.create()
val mapper = component.getUserMapper()
val user = User(1, "John Doe", "john@example.com")
val dto = mapper.toDto(user)
}Check out the demo videos in the examples/media directory:
sampleApplication run configurationexamples/sampleApplication/iosApp/iosApp.xcworkspace
./gradlew :examples:sampleApplication:composeApp:runCopyright 2023
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
Contributions are welcome! Please feel free to submit a Pull Request.