weblog

技術的なメモ置き場。

Kotlinでdataクラスのマッピング

Kotlinでdataクラスのマッピングするためのメモ。
Beanマッピングでおなじみの(?)MapStructを使用する。

Kotlin: 1.3.61
MapStruct: 1.3.1.Final

MapStruct

公式サイトにKotlinのサンプルがある。

github.com

build.gradleに以下を追加する。

dependencies {
    // omit...
    implementation 'org.mapstruct:mapstruct:1.3.1.Final'
    kapt 'org.mapstruct:mapstruct-processor:1.3.1.Final'
}

dataクラスを用意する。
各プロパティは mutable かつ nullable で、引数なしのセカンダリコンストラクタを用意する必要がある。

data class Person(
    var firstName: String?,
    var lastName: String?
) {
    constructor() : this(null, null)
}

data class PersonDto(
    var firstName: String?,
    var lastName: String?
) {
    constructor() : this(null, null)
}

Mapperクラスを用意する。

@Mapper
interface PersonMapper {
    fun map(dto: PersonDto): Person
    fun map(person: Person): PersonDto
}

以上で準備は完了。動かしてみるとマッピングしてくれる。

fun main() {
    val mapper = Mappers.getMapper(PersonMapper::class.java)

    val dto = PersonDto("家康", "徳川")
    val person = mapper.map(dto)
    println(person) // -> Person(firstName=家康, lastName=徳川)

    val personDto = mapper.map(person)
    println(personDto) // -> PersonDto(firstName=家康, lastName=徳川)
}

mapstruct-kotlin

マッピングできたのは良いが、MapStructを使うために mutable やnullable にしたり、セカンダリコンストラクタの用意が必要だったり使うには少しためらわれる。 この問題を解決するのが mapstruct-kotlin 。

github.com

build.gradleに以下を追加する。

dependencies {
    // omit...
    api 'com.github.pozo:mapstruct-kotlin:1.3.1.1'
    kapt 'com.github.pozo:mapstruct-kotlin-processor:1.3.1.1'
}

dataクラスに @KotlinBuilder を付与する。

@KotlinBuilder
data class Person(
    val firstName: String,
    val lastName: String
)

@KotlinBuilder
data class PersonDto(
    val firstName: String,
    val lastName: String
)

MapperクラスはそのままでOK。

@Mapper
interface PersonMapper {
    fun map(dto: PersonDto): Person
    fun map(person: Person): PersonDto
}

以上で準備は完了。期待通りマッピングしてくれる。

fun main() {
    val mapper = Mappers.getMapper(PersonMapper::class.java)

    val dto = PersonDto("家康", "徳川")
    val person = mapper.map(dto)
    println(person) // -> Person(firstName=家康, lastName=徳川)

    val personDto = mapper.map(person)
    println(personDto) // -> PersonDto(firstName=家康, lastName=徳川)
}