Custom Converters¶
Use @MapUsing inside a @MapConfig-annotated object to provide custom conversion logic for individual target properties. Kraft calls your function during code generation and wires the result into the generated constructor call.
See also: Configuration Objects for a full overview of @MapConfig-based mapping.
Property-Source Mode¶
When source is specified, the function receives the value of that single source property and converts it to the target type.
Regular Function¶
A regular (non-extension) function receives the source property value as its parameter.
data class Src(val count: Int)
data class Dst(val label: String)
@MapConfig(source = Src::class, target = Dst::class)
object SrcMapper {
@MapUsing(source = "count", target = "label")
fun convert(v: Int): String = v.toString()
}
Generated: label = SrcMapper.convert(this.count)
Extension Function¶
An extension function receives the source property value as this.
@MapConfig(source = Src::class, target = Dst::class)
object SrcMapper {
@MapUsing(source = "count", target = "label")
fun Int.toLabel(): String = this.toString()
}
Generated: label = with(SrcMapper) { this@toDst.count.toLabel() }
Multiple Converters¶
You can define multiple @MapUsing functions in the same config object, each targeting a different property:
data class Src(val a: Int, val b: Int)
data class Dst(val x: String, val y: String)
@MapConfig(source = Src::class, target = Dst::class)
object SrcMapper {
@MapUsing(source = "a", target = "x")
fun convertA(v: Int): String = "a:$v"
@MapUsing(source = "b", target = "y")
fun convertB(v: Int): String = "b:$v"
}
Generated:
Whole-Source Mode¶
When source is omitted (or left blank), the function receives the entire source object. This is useful for computing a target value from multiple source properties.
Regular Function¶
data class Src(val a: Int, val b: Int)
data class Dst(val combined: String)
@MapConfig(source = Src::class, target = Dst::class)
object SrcMapper {
@MapUsing(target = "combined")
fun combine(src: Src): String = "${src.a}-${src.b}"
}
Generated: combined = SrcMapper.combine(this)
Extension Function¶
@MapConfig(source = Src::class, target = Dst::class)
object SrcMapper {
@MapUsing(target = "combined")
fun Src.combine(): String = "${this.a}-${this.b}"
}
Generated: combined = with(SrcMapper) { this@toDst.combine() }
Type Matching Rules¶
- Property-source mode: the function parameter type must match the source property type exactly (including nullability)
- Whole-source mode: the function parameter type (or extension receiver) must match the source class type
- The function return type must match the target constructor parameter type exactly
- Nullable parameters are supported when the source property is nullable
- Generic types (e.g.,
List<String>) are supported and matched including type arguments
Nullable Parameter Example¶
data class Src(val name: String?)
data class Dst(val label: String)
@MapConfig(source = Src::class, target = Dst::class)
object SrcMapper {
@MapUsing(source = "name", target = "label")
fun convert(v: String?): String = v ?: ""
}
Generated: label = SrcMapper.convert(this.name)
Note: a nullable parameter is only valid when the source property is also nullable. A String? parameter with a non-null String source property produces a compile-time error.
Generic Type Example¶
data class Src(val tags: List<String>)
data class Dst(val tagStr: String)
@MapConfig(source = Src::class, target = Dst::class)
object SrcMapper {
@MapUsing(source = "tags", target = "tagStr")
fun convert(tags: List<String>): String = tags.joinToString()
}
Generated: tagStr = SrcMapper.convert(this.tags)
Direction Parameter¶
When using @MapReverse, you may need both a forward and a reverse converter for the same property. The direction parameter on @MapUsing controls which mapping direction the converter applies to.
| Value | Meaning |
|---|---|
ConverterDirection.AUTO (default) |
Kraft infers the direction from the converter's parameter type |
ConverterDirection.FORWARD |
Converter is used only for source -> target |
ConverterDirection.REVERSE |
Converter is used only for target -> source |
Auto-Detection¶
By default (AUTO), Kraft matches the converter's parameter type against the source property types of each direction. This works automatically when the types differ between source and target:
data class Entity(val id: Int, val name: String)
data class Dto(val id: String, val name: String)
@MapReverse
@MapConfig(source = Entity::class, target = Dto::class)
object EntityMapper {
@MapUsing(source = "id", target = "id") // param Int matches Entity.id -> forward
fun intToString(v: Int): String = v.toString()
@MapUsing(source = "id", target = "id") // param String matches Dto.id -> reverse
fun stringToInt(v: String): Int = v.toInt()
}
Explicit Direction¶
Use explicit direction when auto-detection cannot disambiguate (e.g. both sides have the same type for a property) or when you prefer to be explicit:
@MapUsing(source = "id", target = "id", direction = ConverterDirection.FORWARD)
fun forwardConvert(v: Int): String = v.toString()
@MapUsing(source = "id", target = "id", direction = ConverterDirection.REVERSE)
fun reverseConvert(v: String): Int = v.toInt()
See Reverse Mapping -- Converters in Reverse for the full reverse converter workflow.
Error Cases¶
| Condition | Result |
|---|---|
target is blank or empty |
Compile-time error |
source property name does not exist on the source class |
Compile-time error |
target property name does not exist on the target class |
Compile-time error |
| Parameter type does not match the source property type | Compile-time error ("mismatch") |
| Return type does not match the target constructor parameter type | Compile-time error ("mismatch") |
| Nullable parameter with non-nullable source property | Compile-time error ("mismatch") |
Generic type argument mismatch (e.g., List<Int> vs List<String>) |
Compile-time error ("mismatch") |
Two @MapUsing functions targeting the same property in the same direction |
Compile-time error ("Multiple") |
| Whole-source function parameter type does not match the source class | Compile-time error ("source class") |
Explicit direction mismatches the converter's types |
Compile-time error ("mismatch") |