我想将一些“数据”类对象转换/映射为类似的“数据”类对象。例如,Web表单的类到数据库记录的类。
data class PersonForm(
val firstName: String,
val lastName: String,
val age: Int,
// maybe many fields exist here like address, card number, etc.
val tel: String
)
// maps to ...
data class PersonRecord(
val name: String, // "${firstName} ${lastName}"
val age: Int, // copy of age
// maybe many fields exist here like address, card number, etc.
val tel: String // copy of tel
)
我将ModelMapper用于Java中的此类工作,但是由于数据类是最终的(ModelMapper创建CGLib代理以读取映射定义),因此无法使用它。当我们打开这些类/字段时,可以使用ModelMapper,但必须手动实现“数据”类的功能。
(请参阅ModelMapper示例:https://github.com/jhalterman/modelmapper/blob/master/examples/src/main/java/org/modelmapper/gettingstarted/GettingStartedExample.java)
如何在Kotlin中映射此类“数据”对象?
更新:
ModelMapper自动映射具有相同名称的字段(例如tel-> tel),而无需映射声明。我想用Kotlin的数据类来做。
更新:
每个类的目的取决于哪种类型的应用程序,但是这些类可能放置在应用程序的不同层中。
例如:
这些类是相似的,但不相同。
由于以下原因,我想避免正常的函数调用:
当然,可以使用具有类似功能的库,但是也欢迎使用Kotlin功能的信息(例如在ECMAScript中传播)。
最佳答案
fun PersonForm.toPersonRecord() = PersonRecord(
name = "$firstName $lastName",
age = age,
tel = tel
)
fun PersonForm.toPersonRecord() = with(PersonRecord::class.primaryConstructor!!) {
val propertiesByName = PersonForm::class.memberProperties.associateBy { it.name }
callBy(args = parameters.associate { parameter ->
parameter to when (parameter.name) {
"name" -> "$firstName $lastName"
else -> propertiesByName[parameter.name]?.get(this@toPersonRecord)
}
})
}
open class Transformer<in T : Any, out R : Any>
protected constructor(inClass: KClass<T>, outClass: KClass<R>) {
private val outConstructor = outClass.primaryConstructor!!
private val inPropertiesByName by lazy {
inClass.memberProperties.associateBy { it.name }
}
fun transform(data: T): R = with(outConstructor) {
callBy(parameters.associate { parameter ->
parameter to argFor(parameter, data)
})
}
open fun argFor(parameter: KParameter, data: T): Any? {
return inPropertiesByName[parameter.name]?.get(data)
}
}
val personFormToPersonRecordTransformer = object
: Transformer<PersonForm, PersonRecord>(PersonForm::class, PersonRecord::class) {
override fun argFor(parameter: KParameter, data: PersonForm): Any? {
return when (parameter.name) {
"name" -> with(data) { "$firstName $lastName" }
else -> super.argFor(parameter, data)
}
}
}
fun PersonForm.toPersonRecord() = personFormToPersonRecordTransformer.transform(this)
data class PersonForm(val map: Map<String, Any?>) {
val firstName: String by map
val lastName: String by map
val age: Int by map
// maybe many fields exist here like address, card number, etc.
val tel: String by map
}
// maps to ...
data class PersonRecord(val map: Map<String, Any?>) {
val name: String by map // "${firstName} ${lastName}"
val age: Int by map // copy of age
// maybe many fields exist here like address, card number, etc.
val tel: String by map // copy of tel
}
fun PersonForm.toPersonRecord() = PersonRecord(HashMap(map).apply {
this["name"] = "${remove("firstName")} ${remove("lastName")}"
})