Skip to content

Basic Mapping

This page covers the core mapping annotations and how Kraft matches properties between source and target classes.

@MapConfig

@MapConfig is placed on a standalone object and references both source and target externally. This keeps mapping logic separate from your data classes.

import com.blu3berry.kraft.config.MapConfig

data class User(val name: String, val age: Int)
data class UserDto(val name: String, val age: Int)

@MapConfig(
    source = User::class,
    target = UserDto::class,
)
object UserMapper

Generated output:

fun User.toUserDto(): UserDto = UserDto(
    name = this.name,
    age = this.age,
)

Property matching is by name and type. Every target constructor parameter that shares a name and type with a source property is mapped automatically.

@MapFrom

@MapFrom is placed on the target class. The source parameter specifies the class to map from.

import com.blu3berry.kraft.mapping.MapFrom

data class User(val name: String, val age: Int)

@MapFrom(User::class)
data class UserDto(val name: String, val age: Int)

Generated output:

fun User.toUserDto(): UserDto = UserDto(
    name = this.name,
    age = this.age,
)

@MapTo

@MapTo is placed on the source class. The target parameter specifies the class to map to.

import com.blu3berry.kraft.mapping.MapTo

@MapTo(UserDto::class)
data class User(val name: String, val age: Int)

data class UserDto(val name: String, val age: Int)

Generated output (identical to the @MapFrom example above):

fun User.toUserDto(): UserDto = UserDto(
    name = this.name,
    age = this.age,
)

When to Use Which

Scenario Recommended annotation
You want mapping logic separate from data classes @MapConfig on an object
You own neither class, or they are generated/third-party @MapConfig on an object
You own the target class (typical DTO pattern) @MapFrom on the target
You own the source class @MapTo on the source

All three produce the same generated code. The difference is only where the annotation lives. You cannot place both @MapFrom and @MapTo on the same class -- doing so results in a compile-time error.

Properties with Default Values

Target properties that have a default value and no matching source property are automatically omitted from the generated constructor call, so Kotlin uses the default. No ignore annotation is needed in this case, and Kraft reports nothing.

data class PersonSource(val name: String)
data class PersonDto(
    val name: String,
    val score: Int = 0  // no matching source property -- uses default
)

@MapConfig(source = PersonSource::class, target = PersonDto::class)
object PersonMapper

Generated output (score is omitted, so PersonSource("Ada").toPersonDto() is PersonDto(name = "Ada", score = 0)):

public fun PersonSource.toPersonDto(): PersonDto = PersonDto(
  name = this.name
)

Nullable properties without a default

A nullable target property with no matching source property and no default gets null:

data class PersonDto(
    val name: String,
    val nickname: String?  // no matching source property, no default
)
public fun PersonSource.toPersonDto(): PersonDto = PersonDto(
  name = this.name,
  nickname = null
)

Kraft also warns with KRAFT-W009, because a renamed source property would otherwise turn into a silent null. Write val nickname: String? = null to make null the intended value. The property is then omitted like any other defaulted property, and the warning goes away.

@MapIgnore

Use @MapIgnore to explicitly skip a target property when using @MapFrom or @MapTo. The property must declare a default value (e.g. = null for nullable types) since Kraft will not supply a value for it. For the config-based equivalent, see Ignore Rules.

import com.blu3berry.kraft.mapping.MapFrom
import com.blu3berry.kraft.mapping.MapIgnore

data class ProductSource(val name: String, val price: Double)

@MapFrom(ProductSource::class)
data class ProductDto(
    val name: String,
    @MapIgnore
    val price: Double = 0.0
)

Generated output:

fun ProductSource.toProductDto(): ProductDto = ProductDto(
    name = this.name,
    // price is ignored
)

Error Cases

Missing source property with no default: If the target has a non-nullable constructor parameter without a default value and there is no matching source property, Kraft reports a compile-time error.

Type mismatch: If a source property and target property share the same name but have incompatible types, Kraft reports a compile-time error. Use a field rename or converter to handle this situation.

See Also