example ejemplo data java kotlin modelmapper

java - ejemplo - modelmapper maven



Mejor forma de asignar objetos de datos de Kotlin a objetos de datos (6)

  1. Más simple (¿mejor?):

    fun PersonForm.toPersonRecord() = PersonRecord( name = "$firstName $lastName", age = age, tel = tel )

  2. Reflexión (no gran rendimiento):

    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. Reflejo en caché (rendimiento aceptable, pero no tan rápido como el n.º 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. Almacenar propiedades en un mapa

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

Quiero convertir / asignar algunos objetos de clase de "datos" a objetos de clase de "datos" similares. Por ejemplo, clases para formularios web a clases para registros de bases de datos.

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 )

Uso ModelMapper para tales trabajos en Java, pero no se puede usar porque las clases de datos son finales (ModelMapper crea proxies CGLib para leer definiciones de mapeo). Podemos usar ModelMapper cuando hacemos estas clases / campos abiertos, pero debemos implementar las características de la clase "datos" manualmente. (Ejemplos de ModelMapper: https://github.com/jhalterman/modelmapper/blob/master/examples/src/main/java/org/modelmapper/gettingstarted/GettingStartedExample.java )

¿Cómo mapear tales objetos de "datos" en Kotlin?

Actualización: ModelMapper mapea automáticamente los campos que tienen el mismo nombre (como tel -> tel) sin declaraciones de mapeo. Quiero hacerlo con la clase de datos de Kotlin.

Actualización: El propósito de cada clase depende del tipo de aplicación, pero es probable que se coloquen en la capa diferente de una aplicación.

Por ejemplo:

  • datos de la base de datos (Entidad de base de datos) a datos para formulario HTML (Modelo / Modelo de vista)
  • Resultado REST API a datos para base de datos

Estas clases son similares, pero no son lo mismo.

Quiero evitar las llamadas a funciones normales por estas razones:

  • Depende del orden de los argumentos. Una función para una clase con muchos campos que tienen el mismo tipo (como String) se romperá fácilmente.
  • Muchas declaraciones son necesarias aunque la mayoría de las asignaciones se pueden resolver con la convención de nomenclatura.

Por supuesto, una biblioteca que tiene una función similar está destinada, pero la información de la función Kotlin también es bienvenida (como la difusión en ECMAScript).


¿De verdad quieres una clase separada para eso? Puede agregar propiedades a la clase de datos original:

data class PersonForm( val firstName: String, val lastName: String, val age: Int, val tel: String ) { val name = "${firstName} ${lastName}" }


¿Esto es lo que estás buscando?

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

y entonces:

val personRecord = PersonRecord.ModelMapper.from(personForm)


Esto funciona usando Gson:

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() )

con valores que no aceptan valores NULL porque Gson usa sun.misc.Unsafe ..


Puede usar ModelMapper para mapear a una clase de datos de Kotlin. Las claves son:

  • Use @JvmOverloads (genera un constructor sin argumentos)
  • Valores predeterminados para miembro de la clase de datos
  • Miembro mutable, var en lugar de val

    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)


Use MapStruct:

@Mapper interface PersonConverter { @Mapping(source = "phoneNumber", target = "phone") fun convertToDto(person: Person) : PersonDto @InheritInverseConfiguration fun convertToModel(personDto: PersonDto) : Person }

Utilizar:

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)

https://github.com/mapstruct/mapstruct-examples/tree/master/mapstruct-kotlin