Java 将Kotlin数据对象映射到数据对象的更好方法
我想将一些“数据”类对象转换/映射为类似的“数据”类对象。例如,web窗体的类可以转换为数据库记录的类Java 将Kotlin数据对象映射到数据对象的更好方法,java,kotlin,modelmapper,Java,Kotlin,Modelmapper,我想将一些“数据”类对象转换/映射为类似的“数据”类对象。例如,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 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,但必须手动实现“data”类的功能。
(参见ModelMapper示例:)
如何在Kotlin中映射这些“数据”对象
更新:
ModelMapper自动映射具有相同名称的字段(如tel->tel),而无需映射声明。我想用Kotlin的数据类来实现它
更新:
每个类的用途取决于应用程序的类型,但它们可能位于应用程序的不同层
例如:
- 从数据库(数据库实体)到HTML表单(模型/视图模型)的数据
- 将REST API结果转换为数据库的数据
- 这取决于论点的顺序。一个类的函数包含许多具有相同类型(如字符串)的字段,很容易被破坏
- 许多声明都是必需的,尽管大多数映射可以通过命名约定来解决
当然,我们希望有一个具有类似功能的库,但也欢迎提供Kotlin功能的信息(如在ECMAScript中传播)。这是您想要的吗
data class PersonRecord(val name: String, val age: Int, val tel: String){
object ModelMapper {
fun from(form: PersonForm) =
PersonRecord(form.firstName + form.lastName, form.age, form.tel)
}
}
然后:
val personRecord = PersonRecord.ModelMapper.from(personForm)
你真的想要一个单独的课程吗?可以向原始数据类添加属性:
data class PersonForm(
val firstName: String,
val lastName: String,
val age: Int,
val tel: String
) {
val name = "${firstName} ${lastName}"
}
开放式变压器
受保护的构造函数(包括类:KClass,外类:KClass){
private val outConstructor=outClass.primaryConstructor!!
lazy提供的Properties ByName中的私有值{
inClass.memberProperties.associateBy{it.name}
}
有趣的转换(数据:T):R=with(outConstructor){
callBy(parameters.associate{parameter->
参数到argFor(参数,数据)
})
}
open fun argFor(参数:KParameter,数据:T):有吗{
返回inPropertiesByName[参数.name]?.get(数据)
}
}
val personFormToPersonRecordTransformer=对象
:Transformer(PersonForm::class,PersonRecord::class){
覆盖参数(参数:KParameter,数据:PersonForm):有吗{
返回时间(parameter.name){
“name”->带有(数据){“$firstName$lastName”}
else->super.argFor(参数、数据)
}
}
}
fun PersonForm.toPersonRecord()=personFormToPersonRecordTransformer.transform(此)
数据类PersonForm(val-map:map){
val firstName:映射字符串
val lastName:按映射的字符串
val年龄:按地图整型
//这里可能有很多字段,如地址、卡号等。
瓦尔电话:按地图排列
}
//映射到。。。
数据类PersonRecord(val映射:映射){
val name:String by map/“${firstName}${lastName}”
val age:Int by map//age的副本
//这里可能有很多字段,如地址、卡号等。
val tel:按地图字符串//tel的副本
}
fun PersonForm.toPersonRecord()=PersonRecord(HashMap(map).apply{
此[“名称”]=“${remove(“firstName”)}${remove(“lastName”)}”
})
inline fun <reified T : Any> Any.mapTo(): T =
GsonBuilder().create().run {
toJson(this@mapTo).let { fromJson(it, T::class.java) }
}
fun PersonForm.toRecord(): PersonRecord =
mapTo<PersonRecord>().copy(
name = "$firstName $lastName"
)
fun PersonRecord.toForm(): PersonForm =
mapTo<PersonForm>().copy(
firstName = name.split(" ").first(),
lastName = name.split(" ").last()
)
inline fun Any.mapTo():T=
GsonBuilder().create().run{
toJson(this@mapTo).let{fromJson(it,T::class.java)}
}
有趣的PersonForm.toRecord():PersonRecord=
mapTo().copy(
name=“$firstName$lastName”
)
有趣的PersonRecord.toForm():PersonForm=
mapTo().copy(
firstName=name.split(“”.first(),
lastName=name.split(“”.last()
)
由于Gson使用..MapStruct,因此不允许将不可为null的值设为null,这样kapt就可以生成进行映射的类(无反射) 使用MapStruct:
@Mapper
interface PersonConverter {
@Mapping(source = "phoneNumber", target = "phone")
fun convertToDto(person: Person) : PersonDto
@InheritInverseConfiguration
fun convertToModel(personDto: PersonDto) : Person
}
// Note this either needs empty constructor or we need @KotlinBuilder as dsecribe below
data class Person: this(null, null, null, null) (...)
使用:
编辑:
现在使用@KotlinBuilder来避免构造函数()问题:
使用@KotlinBuilder
注释数据类。这将创建一个MapStruct使用的PersonBuilder
类,从而避免破坏数据类与构造函数()的接口
依赖关系:
// https://mvnrepository.com/artifact/com.github.pozo/mapstruct-kotlin
api("com.github.pozo:mapstruct-kotlin:1.3.1.1")
kapt("com.github.pozo:mapstruct-kotlin-processor:1.3.1.1")
您可以使用ModelMapper映射到Kotlin数据类。关键是:
- 使用@JVM重载(生成不带参数的构造函数)
- 数据类成员的默认值
- 可变成员,var而不是val
数据类AppSyncEvent@JVM重载构造函数( 变量字段:String=“”, 变量参数:Map=mapOf(), 变量源:Map=mapOf() ) val event=ModelMapper().map(请求,AppSyncEvent::class.java)
var
而不是val
package com.example
annotation class NoArg
@NoArg
data class MyData(var myDatum: String)
mm.map(. . ., MyData::class.java)
在build.gradle中(参见Maven文档):
使用
如果字段名不同,则可能需要一些映射规则。见PS:使用
kotlin no args
plugin使数据类具有默认的no arg构造函数您编写的操作是我想要做的。但是我想减少映射声明,因为存在许多具有相同名称的字段(如tel->tel)。我只想写一些特殊的规则,比如firstName+lastName=>name。请描述一下如何使用映射类。拥有两种不同数据格式的目的是什么?从未听说过数据模型的重复(遗留代码的情况除外)。通常,您正在处理的数据
inline fun <reified T : Any> Any.mapTo(): T =
GsonBuilder().create().run {
toJson(this@mapTo).let { fromJson(it, T::class.java) }
}
fun PersonForm.toRecord(): PersonRecord =
mapTo<PersonRecord>().copy(
name = "$firstName $lastName"
)
fun PersonRecord.toForm(): PersonForm =
mapTo<PersonForm>().copy(
firstName = name.split(" ").first(),
lastName = name.split(" ").last()
)
@Mapper
interface PersonConverter {
@Mapping(source = "phoneNumber", target = "phone")
fun convertToDto(person: Person) : PersonDto
@InheritInverseConfiguration
fun convertToModel(personDto: PersonDto) : Person
}
// Note this either needs empty constructor or we need @KotlinBuilder as dsecribe below
data class Person: this(null, null, null, null) (...)
val converter = Mappers.getMapper(PersonConverter::class.java) // or PersonConverterImpl()
val person = Person("Samuel", "Jackson", "0123 334466", LocalDate.of(1948, 12, 21))
val personDto = converter.convertToDto(person)
println(personDto)
val personModel = converter.convertToModel(personDto)
println(personModel)
@KotlinBuilder
data class Person(
val firstName: String,
val lastName: String,
val age: Int,
val tel: String
)
// https://mvnrepository.com/artifact/com.github.pozo/mapstruct-kotlin
api("com.github.pozo:mapstruct-kotlin:1.3.1.1")
kapt("com.github.pozo:mapstruct-kotlin-processor:1.3.1.1")
data class AppSyncEvent @JvmOverloads constructor(
var field: String = "",
var arguments: Map<String, *> = mapOf<String, Any>(),
var source: Map<String, *> = mapOf<String, Any>()
)
val event = ModelMapper().map(request, AppSyncEvent::class.java)
package com.example
annotation class NoArg
@NoArg
data class MyData(var myDatum: String)
mm.map(. . ., MyData::class.java)
buildscript {
. . .
dependencies {
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion"
}
}
apply plugin: 'kotlin-noarg'
noArg {
annotation "com.example.NoArg"
}
/** Util.kt **/
class MapperDto() : ModelMapper() {
init {
configuration.matchingStrategy = MatchingStrategies.LOOSE
configuration.fieldAccessLevel = Configuration.AccessLevel.PRIVATE
configuration.isFieldMatchingEnabled = true
configuration.isSkipNullEnabled = true
}
}
object Mapper {
val mapper = MapperDto()
inline fun <S, reified T> convert(source: S): T = mapper.map(source, T::class.java)
}
val form = PersonForm(/** ... **/)
val record: PersonRecord = Mapper.convert(form)