java - 将 Kotlin 数据对象映射到数据对象的更好方法

我想将一些“数据”类对象转换/映射为类似的“数据”类对象。例如,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
)

我在 Java 中将 ModelMapper 用于此类工作,但它不能使用,因为数据类是最终的(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 的数据类来做。

更新: 每个类的用途取决于应用程序的类型,但它们可能被放置在应用程序的不同层中。

例如:

  • 从数据库(数据库实体)到 HTML 表单数据(模型/ View 模型)的数据
  • REST API 结果到数据库数据

这些类相似,但又不相同。

出于以下原因,我想避免正常的函数调用:

  • 这取决于参数的顺序。具有许多具有相同类型(如字符串)的字段的类的函数将很容易被破坏。
  • 许多声明是必要的,尽管大多数映射可以通过命名约定来解决。

当然,想要一个具有类似功能的库,但也欢迎 Kotlin 功能的信息(如在 ECMAScript 中传播)。

最佳答案

  1. 最简单(最好的?):

    fun PersonForm.toPersonRecord() = PersonRecord(
            name = "$firstName $lastName",
            age = age,
            tel = tel
    )
    
  2. 反射(reflection)(表现不佳):

    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)
            }
        })
    }
    
  3. 缓存反射(性能不错,但不如 #1 快):

    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)
    
  4. Storing Properties in a Map

    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")}"
    })
    

https://stackoverflow.com/questions/39199426/

相关文章:

kotlin - 如何在 Kotlin 中写入文件?

kotlin - 有什么区别!!和 ?在 Kotlin ?

kotlin - 如何将项目添加到 Kotlin 中的 ArrayList?

android - 单元测试室和 LiveData

loops - 在 Kotlin 中的功能循环中,如何执行 "break"或 "continue"?

kotlin - 如何从命令行运行 Kotlin 类?

android - kotlin-stdlib-jre7 已弃用。请改用 kotlin-stdlib

kotlin - Kotlin 协程中的挂起函数是什么意思?

kotlin - 警告 : Kotlin runtime JAR files in the clas

kotlin - 如何在 Kotlin 中迭代 hashmap?