The Kotlin Object Multiplatform Mapper provides you a possibility to generate (via KSP) extension function to map one object to another.
- Supports KSP Multiplatform
- Maps as constructor parameters as well as public properties with setter
- Supports properties' types cast
- Supports Java objects get* functions
- Supports multi-source classes with separated configurations
- Has next properties annotations:
- Specify mapping from property with different name
- Specify a converter to map data from source unusual way
- Specify a resolver to map default values into properties
- Specify null substitute to map nullable properties into not-nullable
 
- Support extension via plugins
- JVM
- JavaScript
- Linux
- Windows (mingwX64)
- macOS
- iOS
- Iterable Plugin:
- Support collections mapping with different types of elements
 
- Exposed Plugin:
- Support mapping from Exposed Table Object (ResultRow)
 
- Enum Plugin:
- Support Enum mapping with default value annotation
 
plugins {
    id("com.google.devtools.ksp") version "2.0.21-1.0.28"
}
val kommVersion = "0.25.0"
depensencies {
    implementation("com.ucasoft.komm:komm-annotations:$kommVersion")
    ksp("com.ucasoft.komm:komm-processor:$kommVersion")
}plugins {
    id("com.google.devtools.ksp") version "2.0.21-1.0.28"
}
val kommVersion = "0.25.0"
kotlin {
    jvm {
        withJava()
    }
    js(IR) {
        nodejs()
    }
    // Add other platforms
    sourceSets {
        val commonMain by getting {
            dependencies {
                implementation("com.ucasoft.komm:komm-annotations:$kommVersion")
            }
        }
        val jvmMain by getting
        val jsMain by getting
        // Init other sourceSets
    }
}
dependencies {
    add("kspJvm", "com.ucasoft.komm:komm-processor:$kommVersion")
    add("kspJs", "com.ucasoft.komm:komm-processor:$kommVersion")
    // Add other platforms like `kspAndroidNativeX64`, `kspLinuxX64`, `kspMingwX64` etc.
}class SourceObject {
    val id = 150
    val intToString = 300
    val stringToInt = "250"
}
@KOMMMap(from = [SourceObject::class])
data class DestinationObject(
    val id: Int,
    val stringToInt: Int
) {
    var intToString: String = ""
}or
@KOMMMap(to = [DestinationObject::class])
class SourceObject {
    val id = 150
    val intToString = 300
    val stringToInt = "250"
}
data class DestinationObject(
    val id: Int,
    val stringToInt: Int
) {
    var intToString: String = ""
}fun SourceObject.toDestinationObject(): DestinationObject = DestinationObject(
    id = id,
    stringToInt = stringToInt.toInt()
).also { 
    it.intToString = intToString.toString()
}@KOMMMap(
    from = [SourceObject::class],
    config = MapConfiguration(
        tryAutoCast = false
    )
)
data class DestinationObject(
    val id: Int,
    val stringToInt: Int
) {
    var intToString: String = ""
}e: [ksp] com.ucasoft.komm.processor.exceptions.KOMMCastException: AutoCast is turned off! You have to use @MapConvert annotation to cast (stringToInt: Int) from (stringToInt: String)@KOMMMap(
    from = [SourceObject::class],
    config = MapConfiguration(
        convertFunctionName = "convertToDestination"
    )
)
data class DestinationObject(
    val id: Int,
    val stringToInt: Int
) {
    var intToString: String = ""
}fun SourceObject.convertToDestination(): DestinationObject = DestinationObject(
    id = id,
    stringToInt = stringToInt.toInt()
).also { 
    it.intToString = intToString.toString()
}class SourceObject {
    //...
    val userName = "user"
}
@KOMMMap(from = [SourceObject::class])
data class DestinationObject(
    //...
    @MapName("userName")
    val name: String
) {
    var intToString: String = ""
}or
@KOMMMap(to = [DestinationObject::class])
class SourceObject {
    //...
    @MapName("name")
    val userName = "user"
}
data class DestinationObject(
    //...
    val name: String
) {
    var intToString: String = ""
}fun SourceObject.toDestinationObject(): DestinationObject = DestinationObject(
    //...
    name = userName
).also { 
    it.intToString = intToString.toString()
}class CostConverter(source: SourceObject) : KOMMConverter<SourceObject, Double, DestinationObject, String>(source) {
    override fun convert(sourceMember: Double) = "$sourceMember ${source.currency}"
}class SourceObject {
    //...
    val cost = 499.99
}
@KOMMMap(from = [SourceObject::class])
data class DestinationObject(
    //...
    @MapConvert<SourceObject, DestinationObject, CostConverter>(CostConverter::class)
    val cost: String
) {
    //...
    @MapConvert<SourceObject, CostConverter>(CostConverter::class, "cost")
    var otherCost: String = ""
}fun SourceObject.toDestinationObject(): DestinationObject = DestinationObject(
    //...
    cost = CostConverter(this).convert(cost)
).also { 
    //...
    it.otherCost = CostConverter(this).convert(cost)
}class DateResolver(destination: DestinationObject?) : KOMMResolver<DestinationObject, Date>(destination) {
    
    override fun resolve(): Date = Date.from(Instant.now())
}@KOMMMap(from = [SourceObject::class])
data class DestinationObject(
    //...
    @MapDefault<DateResolver>(DateResolver::class)
    val activeDate: Date
) {
    //...
    @MapDefault<DateResolver>(DateResolver::class)
    var otherDate: Date = Date.from(Instant.now())
}fun SourceObject.toDestinationObject(): DestinationObject = DestinationObject(
    //...
    activeDate = DateResolver(null).resolve()
).also { 
    //...
    it.otherDate = DateResolver(it).resolve()
}@KOMMMap(
    from = [SourceObject::class],
    config = MapConfiguration(
      allowNotNullAssertion = true
    )
)
data class DestinationObject(
    val id: Int
)
data class SourceObject(
    val id: Int?
)fun SourceObject.toDestinationObject(): DestinationObject = DestinationObject(
    id = id!!
)e: [ksp] com.ucasoft.komm.processor.exceptions.KOMMCastException: Auto Not-Null Assertion is not allowed! You have to use @NullSubstitute annotation for id property.class IntResolver(destination: DestinationObject?): KOMMResolver<DestinationObject, Int>(destination) {
    override fun resolve() = 1
}@KOMMMap(
    from = [SourceObject::class]
)
data class DestinationObject(
    @NullSubstitute(MapDefault(IntResolver::class))
    val id: Int
) {
    @NullSubstitute(MapDefault(IntResolver::class), "id")
    var otherId: Int = 0
}fun SourceObject.toDestinationObject(): DestinationObject = DestinationObject(
    id = id ?: IntResolver(null).resolve()
).also {
    it.otherId = id ?: IntResolver(it).resolve()
}@KOMMMap(
    to = [DestinationObject::class]
)
data class SourceObject(
    @NullSubstitute(MapDefault(IntResolver::class))
    val id: Int?
) fun SourceObject.toDestinationObject(): DestinationObject = DestinationObject(
    id = id ?: IntResolver(null).resolve()
)@KOMMMap(
    from = [FirstSourceObject::class, SecondSourceObject::class]
)
data class DestinationObject(
    @NullSubstitute(MapDefault(IntResolver::class), [FirstSourceObject::class])
    @MapName("userId", [SecondSourceObject::class])
    val id: Int
) {
    @NullSubstitute(MapDefault(IntResolver::class), "id", [FirstSourceObject::class])
    var otherId: Int = 0
}
data class FirstSourceObject(
  val id: Int?
)
data class SecondSourceObject(
    val userId: Int
)in case, different sources should be configured different:
@KOMMMap(
    from = [FirstSourceObject::class],
    config = MapConfiguration(
        allowNotNullAssertion = true
    )
)
@KOMMMap(
    from = [SecondSourceObject::class]
)
data class DestinationObject(
  @NullSubstitute(MapDefault(IntResolver::class), [FirstSourceObject::class])
  @MapName("userId", [SecondSourceObject::class])
  val id: Int
) {
  @NullSubstitute(MapDefault(IntResolver::class), "id", [FirstSourceObject::class])
  var otherId: Int = 0
}fun FirstSourceObject.toDestinationObject(): DestinationObject = DestinationObject(
    id = id ?: IntResolver(null).resolve()
).also {
    it.otherId = id ?: IntResolver(it).resolve()
}
fun SecondSourceObject.toDestinationObject(): DestinationObject = DestinationObject(
  id = userId
)plugins {
    id("com.google.devtools.ksp") version "2.0.21-1.0.28"
}
val kommVersion = "0.25.0"
depensencies {
    implementation("com.ucasoft.komm:komm-annotations:$kommVersion")
    ksp("com.ucasoft.komm:komm-processor:$kommVersion")
    ksp("com.ucasoft.komm:komm-plugins-iterable:$kommVersion")
}plugins {
    id("com.google.devtools.ksp") version "2.0.21-1.0.28"
}
val kommVersion = "0.25.0"
//...
dependencies {
    add("kspJvm", "com.ucasoft.komm:komm-plugins-iterable:$kommVersion")
    add("kspJvm", "com.ucasoft.komm:komm-processor:$kommVersion")
    add("kspJs", "com.ucasoft.komm:komm-plugins-iterable:$kommVersion")
    add("kspJs", "com.ucasoft.komm:komm-processor:$kommVersion")
    // Add other platforms like `kspAndroidNativeX64`, `kspLinuxX64`, `kspMingwX64` etc.
}class SourceObject {
    val intList: List<Int>? = listOf(1, 2, 3)
}
@KOMMMap(from = [SourceObject::class], config = MapConfiguration(allowNotNullAssertion = true))
data class DestinationObject(
    @MapName("intList")
    val stringList: MutableList<String>
)public fun SourceObject.toDestinationObject(): DestinationObject = DestinationObject(
	stringList = intList!!.map { it.toString() }.toMutableList()
)class SourceObject {
    val intList: List<Int>? = listOf(1, 2, 3)
}
@KOMMMap(from = [SourceObject::class])
data class DestinationObject(
    @NullSubstitute(MapDefault(StringListResolver::class), "intList")
    val stringList: MutableList<String>
)public fun SourceObject.toDestinationObject(): DestinationObject = DestinationObject(
	stringList = intList?.map { it.toString() }?.toMutableList() ?: StringListResolver(null).resolve()
)plugins {
    id("com.google.devtools.ksp") version "2.0.21-1.0.28"
}
val kommVersion = "0.25.0"
depensencies {
    implementation("com.ucasoft.komm:komm-annotations:$kommVersion")
    ksp("com.ucasoft.komm:komm-processor:$kommVersion")
    ksp("com.ucasoft.komm:komm-plugins-exposed:$kommVersion")
}object SourceObject: Table() {
    val id = integer("id").autoIncrement()
    val name = varchar("name", 255)
    val age = integer("age")
    override val primaryKey = PrimaryKey(id)
}
@KOMMMap(from = [SourceObject::class])
data class DestinationObject(
    val id: Int,
    val name: String,
    val age: Int
)public fun ResultRow.toDestinationObject(): DestinationObject = DestinationObject(
    id = this[SourceObject.id],
    name = this[SourceObject.name],
    age = this[SourceObject.age]
)plugins {
    id("com.google.devtools.ksp") version "2.0.21-1.0.28"
}
val kommVersion = "0.25.0"
depensencies {
    implementation("com.ucasoft.komm:komm-annotations:$kommVersion")
    ksp("com.ucasoft.komm:komm-processor:$kommVersion")
    ksp("com.ucasoft.komm:komm-plugins-enum:$kommVersion")
}plugins {
    id("com.google.devtools.ksp") version "2.0.21-1.0.28"
}
val kommVersion = "0.25.0"
//...
dependencies {
    add("kspJvm", "com.ucasoft.komm:komm-plugins-enum:$kommVersion")
    add("kspJvm", "com.ucasoft.komm:komm-processor:$kommVersion")
    add("kspJs", "com.ucasoft.komm:komm-plugins-enum:$kommVersion")
    add("kspJs", "com.ucasoft.komm:komm-processor:$kommVersion")
    // Add other platforms like `kspAndroidNativeX64`, `kspLinuxX64`, `kspMingwX64` etc.
}enum class SourceEnum {
    UP, 
    DOWN,
    LEFT,
    RIGHT
}
data class SourceObject(
    val direction: SourceEnum
)
@KOMMMap(from = [SourceObject::class])
data class DestinationObject(
    val direction: DestinationObject.DestinationEnum
) {
    enum class DestinationEnum {
        UP,
        DOWN,
        OTHER
    }
}fun SourceObject.toDestinationObject(): toDestinationObject = toDestinationObject(
	direction = DestinationObject.DestinationEnum.valueOf(direction.name)
)data class SourceObject(
    val direction: SourceEnum?
)
@KOMMMap(from = [SourceObject::class])
data class DestinationObject(
    @NullSubstitute(MapDefault(DirectionResolver::class))
    val direction: DestinationObject.DestinationEnum
)
class DirectionResolver(destination: DestinationEnum?) : KOMMResolver<DestinationEnum, DestinationObject.DestinationEnum>(destination) {
  override fun resolve() = DestinationObject.DestinationEnum.OTHER
}fun SourceObject.toDestinationObject(): toDestinationObject = toDestinationObject(
	direction = (if (direction != null) DestinationObject.DestinationEnum.valueOf(direction.name) else null)
	  ?: DirectionResolver(null).resolve()
)data class SourceObject(
    val direction: SourceEnum?
)
@KOMMMap(from = [SourceObject::class])
data class DestinationObject(
    @NullSubstitute(MapDefault(DirectionResolver::class))
    @KOMMEnum("OTHER")
    val direction: DestinationObject.DestinationEnum
)
class DirectionResolver(destination: DestinationEnum?) : KOMMResolver<DestinationEnum, DestinationObject.DestinationEnum>(destination) {
  override fun resolve() = DestinationObject.DestinationEnum.OTHER
}fun SourceObject.toDestinationObject(): toDestinationObject = toDestinationObject(
	direction = (if (direction != null) DestinationObject.DestinationEnum.valueOf(if
    (DestinationObject.DestinationEnum.entries.any { it.name == direction.name }) direction.name else "OTHER")
    else null) ?: DirectionResolver(null).resolve()
)