pozo / mapstruct-kotlin Goto Github PK
View Code? Open in Web Editor NEWUsing mapstruct with kotlin data classes.
License: MIT License
Using mapstruct with kotlin data classes.
License: MIT License
Add support for Gradle's incremental annotation processing with Kapt should help speed up build times. MapStruct is planning on this for 1.4.0, mapstruct/mapstruct#1971.
Currently kotlin users need to disable it with
kapt.incremental.apt=false
Theoretically, you'll likely just need to add a META-INF/services file that suggests that annotation processor is incremental.
src/main/resources/META-INF/gradle/incremental.annotation.processors
com.github.pozo.BuilderProcessor,isolating
https://docs.gradle.org/6.3/userguide/java_plugin.html#making_an_annotation_processor_incremental
ex)
models
abstract class Audit(
val createdBy: String = "", val createdAt: OffsetDateTime = OffsetDateTime.now(),
val lastModifiedBy: String = "", val lastModifiedAt: OffsetDateTime = OffsetDateTime.now()
) : Serializable
@KotlinBuilder
data class UserGroup(
val id: Long = -1,
val name: String = "",
val roles: List<Role> = emptyList()
) : Audit()
generated builder class
package com.kakao.mail.gateman.common.model;
import java.lang.String;
import java.util.List;
public final class UserGroupBuilder {
private long id;
private String name;
private List<Role> roles;
public UserGroupBuilder setId(long id) {
this.id = id;
return this;
}
public UserGroupBuilder setName(String name) {
this.name = name;
return this;
}
public UserGroupBuilder setRoles(List<Role> roles) {
this.roles = roles;
return this;
}
public UserGroup create() {
return new UserGroup(id, name, roles);
}
public static UserGroupBuilder builder() {
return new UserGroupBuilder();
}
}
Therefore, it is impossible to map the suepr class field.
I have a strange issue where it fails to generate a mapper. I'm trying to create nested objects from multiple parameters which works fine as long as I don't user multiple parameters for a nested object.
See example UserMapper.kt:37
The first 4 mappings are generated without problems but the 5th does somehow not work.
The same works fine in Java see UserMapper.java:22
Hi, please add to the readme examples of configuration for maven
Hi,
Do you think the library is good for a stable release alongside MapStruct 1.4.0? I've using it for 4 months without an issue :)
Hi,
A MapStruct/mapstruct-kotlin issue:
When I forget to create my class as data class, but apply @KotlinBuilder then neither KotlinBuilder nor MapStruct produce an error during build, but MapStruct does not build the mapper. It just fails silently to do anything. This made it difficult to track down such issues.
If possible and easy, it would be really nice if @KotlinBuilder just fails if annotation is applied to non-data-class. Without @KotlinBuilder, MapStruct gives fine error about missing constructor.
Best Alex
Versions
api 'org.mapstruct:mapstruct:1.3.1.Final'
kapt 'org.mapstruct:mapstruct-processor:1.3.1.Final'
api "com.github.pozo:mapstruct-kotlin:1.3.1.1"
kapt "com.github.pozo:mapstruct-kotlin-processor:1.3.1.1"
I have an object that is generically typed, for example:
import com.github.pozo.KotlinBuilder
interface Bar
@KotlinBuilder
data class Foo<T : Bar>(
val bar: T
)
When I build this, it generates the following:
public final class FooBuilder {
private T bar;
public FooBuilder setBar(T bar) {
this.bar = bar;
return this;
}
public Foo create() {
return new Foo(bar);
}
public static FooBuilder builder() {
return new FooBuilder();
}
}
But it also dumps the following error:
private T bar;
^
symbol: class T
location: class FooBuilder/.../FooBuilder.java:6: error: cannot find symbol
public FooBuilder setBar(T bar) {
^
symbol: class T
location: class FooBuilder
This seems very close and is simply missing the same type restriction on the Builder class.
Hi,
thanks for your great work.
I've observed that for classes with isXXX properties the generated mapper doesn't include mapping of such properties.
For the following class
@KotlinBuilder
data class Animal(
val isDog: Boolean
)
isDog
property will not be mapped in the mapper.
I believe it might be caused by the difference in getter naming
For such a class isDog()
method will be generated on the bytecode level (and setDog(boolean var)
for var properties) However from what I see a setIsDog()
method will be generated by the KotlinBuilder. There is a mismatch that can cause the MappingProcessor not to find the proper setter and just omitting the property.
Given a data class with a parameter named default
, it get's mapped as a weird property name, like p0_772401952
, where the p#
part seems related to the index in which the parameter is in the constructor and 772401952
is some static int.
package com.pureport.kato.api
import com.github.pozo.KotlinBuilder
@KotlinBuilder
data class Foo(
val default: Boolean
)
@KotlinBuilder
data class Foo2(
val bar: String = "",
val default: String
)
@KotlinBuilder
data class Foo3(
val default: String,
val bar: String = ""
)
package com.pureport.kato.api;
public final class FooBuilder {
private boolean p0_772401952;
public FooBuilder setP0_772401952(boolean p0_772401952) {
this.p0_772401952 = p0_772401952;
return this;
}
public Foo create() {
return new Foo(p0_772401952);
}
public static FooBuilder builder() {
return new FooBuilder();
}
}
package com.pureport.kato.api;
import java.lang.String;
public final class Foo2Builder {
private String bar;
private String p1_772401952;
public Foo2Builder setBar(String bar) {
this.bar = bar;
return this;
}
public Foo2Builder setP1_772401952(String p1_772401952) {
this.p1_772401952 = p1_772401952;
return this;
}
public Foo2 create() {
return new Foo2(bar, p1_772401952);
}
public static Foo2Builder builder() {
return new Foo2Builder();
}
}
package com.pureport.kato.api;
import java.lang.String;
public final class Foo3Builder {
private String p0_772401952;
private String bar;
public Foo3Builder setP0_772401952(String p0_772401952) {
this.p0_772401952 = p0_772401952;
return this;
}
public Foo3Builder setBar(String bar) {
this.bar = bar;
return this;
}
public Foo3 create() {
return new Foo3(p0_772401952, bar);
}
public static Foo3Builder builder() {
return new Foo3Builder();
}
}
Hi
I think this project is very useful.
I have question.
How can I solve this if we write JPA entity as class (not data class)?
@KotlinBuilder annotation is only available in data class.
TYSM
Given:
@KotlinBuilder
data class A(var b: B?) {
data class B(var name: String)
}
Expected:
public final class ContactInfoBuilder {
public static CallbackApplication.ContactInfo builder() {
return new CallbackApplication.ContactInfo();
}
}
Actual:
public final class ContactInfoBuilder {
public static ContactInfo builder() {
return new ContactInfo();
}
}
Useful links:
https://stackoverflow.com/questions/34271401/how-to-generate-inner-class-in-javapoet-in-java
I suspect this is MapStruct issue, so master is here:
PS: I much appreciate the @KotlinBuilder, good work on that front.
Hi
Wie does the KotlinBuilder annotation have runtime retention? Wouldn't source be enough for an annotation processor?
When I add secondary constructor to data class, at builder class are two setter of the property.
My class
@KotlinBuilder
data class ProjectGroupModel(
val parent: ProjectModel,
val children: List<ProjectModel>
) : BaseAggregateRoot() {
constructor(parent: ProjectModel) : this(parent, emptyList())
The result code
public final class ProjectGroupModelBuilder {
private ProjectModel parent;
private List<ProjectModel> children;
private ProjectModel parent;
public ProjectGroupModelBuilder setParent(ProjectModel parent) {
this.parent = parent;
return this;
}
public ProjectGroupModelBuilder setChildren(List<ProjectModel> children) {
this.children = children;
return this;
}
public ProjectGroupModelBuilder setParent(ProjectModel parent) {
this.parent = parent;
return this;
}
public ProjectGroupModel create() {
return new ProjectGroupModel(parent, children, parent);
}
public static ProjectGroupModelBuilder builder() {
return new ProjectGroupModelBuilder();
}
}
As result I've got compilation error
ProjectGroupModelBuilder.java:10: error: variable parent is already defined in class ProjectGroupModelBuilder
Hi,
This project seems to be first Kotlin-friendly attempt to model mapping. Since there is a new (final) release of MapStruct 1.3.0 do you have any plans to publish this library to the public?
I have an issue with Kotlin -> kapt -> mapstruct
I have 2 data classes
@entity(name = "PLAN")
@typedef(name = "pgsql_enum",
typeClass = PostgreSQLEnumType::class)
@KotlinBuilder
data class Plan(
@Id
@GeneratedValue(generator = "UUID")
@GenericGenerator(name = "UUID", strategy = "org.hibernate.id.UUIDGenerator")
@JsonProperty("uuid")
val uuid: UUID? = null,
@Column(nullable = false)
val name: String,
...
@document(collection = "plan")
@KotlinBuilder
data class MongoPlan(
val uuid: String? = null,
val name: String,
....
and mapping instructions
@Mapper
interface MongoPlanMapper {
@Mappings(Mapping(target = "uuid",
qualifiedByName = ["uuidMapper"]))
fun convert(plan: Plan): MongoPlan
@Named("uuidMapper")
fun uuidMapper(uuid: UUID): String =
uuid.toString()
}
and I get
Error: java:".
TL;DR:
@Mapper
interface JobPostingMapper {
@Mappings
fun map(response: QuestionsResponse): Questions
@Mappings
fun map(model: Questions): QuestionsResponse
}
data class Questions(
val questionSchema: QuestionSchema,
val questionUiSchema: Map<String, Any>,
val answer: Map<String, Any>
)
@Serializable
data class QuestionsResponse(
@SerialName("question_schema")
val questionSchema: QuestionSchema,
@SerialName("question_ui_schema")
val questionUiSchema: Map<String, JsonElement>,
val answer: Map<String, JsonElement>
)
Does it possible to map different data type? as shown above JsonElement
in DTO, Any
in DOMAIN.
Because when trying the above case, error log thrown instead
JobPostingMapper.java:11: error: Internal error in the mapping processor: java.lang.IndexOutOfBoundsException: Index: 0, Size: 0 at java.util.ArrayList.rangeCheck(ArrayList.java:659) at java.util.ArrayList.get(ArrayList.java:435) at org.mapstruct.ap.internal.model.common.Type$TypeVarMatcher.visitDeclared(Type.java:1165) at org.mapstruct.ap.internal.model.common.Type$TypeVarMatcher.visitDeclared(Type.java:1141) at com.sun.tools.javac.code.Type$ClassType.accept(Type.java:944) at javax.lang.model.util.AbstractTypeVisitor6.visit(AbstractTypeVisitor6.java:92) at org.mapstruct.ap.internal.model.common.Type.resolveTypeVarToType(Type.java:1136) at org.mapstruct.ap.internal.model.source.builtin.BuiltInMethod.matches(BuiltInMethod.java:70) at org.mapstruct.ap.internal.model.source.selector.TypeSelector.lambda$getMatchingParameterBinding$0(TypeSelector.java:158) at java.util.ArrayList.removeIf(ArrayList.java:1415) at org.mapstruct.ap.internal.model.source.selector.TypeSelector.getMatchingParameterBinding(TypeSelector.java:157) at org.mapstruct.ap.internal.model.source.selector.TypeSelector.getMatchingMethods(TypeSelector.java:71) at org.mapstruct.ap.internal.model.source.selector.MethodSelectors.getMatchingMethods(MethodSelectors.java:62) at org.mapstruct.ap.internal.processor.creation.MappingResolverImpl$ResolvingAttempt.getBestMatch(MappingResolverImpl.java:452) at org.mapstruct.ap.internal.processor.creation.MappingResolverImpl$ResolvingAttempt.getTargetAssignment(MappingResolverImpl.java:263) at org.mapstruct.ap.internal.processor.creation.MappingResolverImpl$ResolvingAttempt.access$100(MappingResolverImpl.java:158) at org.mapstruct.ap.internal.processor.creation.MappingResolverImpl.getTargetAssignment(MappingResolverImpl.java:139) at org.mapstruct.ap.internal.model.MapMappingMethod$Builder.build(MapMappingMethod.java:137) at org.mapstruct.ap.internal.model.PropertyMapping$PropertyMappingBuilder.forgeMapMapping(PropertyMapping.java:636) at org.mapstruct.ap.internal.model.PropertyMapping$PropertyMappingBuilder.forge(PropertyMapping.java:295) at org.mapstruct.ap.internal.processor.creation.MappingResolverImpl$ResolvingAttempt.getTargetAssignment(MappingResolverImpl.java:305) at org.mapstruct.ap.internal.processor.creation.MappingResolverImpl$ResolvingAttempt.access$100(MappingResolverImpl.java:158) at org.mapstruct.ap.internal.processor.creation.MappingResolverImpl.getTargetAssignment(MappingResolverImpl.java:139) at org.mapstruct.ap.internal.model.PropertyMapping$PropertyMappingBuilder.build(PropertyMapping.java:243) at org.mapstruct.ap.internal.model.BeanMappingMethod$Builder.applyPropertyNameBasedMapping(BeanMappingMethod.java:1211) at org.mapstruct.ap.internal.model.BeanMappingMethod$Builder.applyPropertyNameBasedMapping(BeanMappingMethod.java:1175) at org.mapstruct.ap.internal.model.BeanMappingMethod$Builder.build(BeanMappingMethod.java:259) at org.mapstruct.ap.internal.processor.MapperCreationProcessor.getMappingMethods(MapperCreationProcessor.java:375) at org.mapstruct.ap.internal.processor.MapperCreationProcessor.getMapper(MapperCreationProcessor.java:152) at org.mapstruct.ap.internal.processor.MapperCreationProcessor.process(MapperCreationProcessor.java:123) at org.mapstruct.ap.internal.processor.MapperCreationProcessor.process(MapperCreationProcessor.java:74) at org.mapstruct.ap.MappingProcessor.process(MappingProcessor.java:338) at org.mapstruct.ap.MappingProcessor.processMapperTypeElement(MappingProcessor.java:318) at org.mapstruct.ap.MappingProcessor.processMapperElements(MappingProcessor.java:267) at org.mapstruct.ap.MappingProcessor.process(MappingProcessor.java:166) at org.jetbrains.kotlin.kapt3.base.incremental.IncrementalProcessor.process(incrementalProcessors.kt) at org.jetbrains.kotlin.kapt3.base.ProcessorWrapper.process(annotationProcessing.kt:147) at com.sun.tools.javac.processing.JavacProcessingEnvironment.callProcessor(JavacProcessingEnvironment.java:802) at com.sun.tools.javac.processing.JavacProcessingEnvironment.discoverAndRunProcs(JavacProcessingEnvironment.java:713) at com.sun.tools.javac.processing.JavacProcessingEnvironment.access$1800(JavacProcessingEnvironment.java:91) at com.sun.tools.javac.processing.JavacProcessingEnvironment$Round.run(JavacProcessingEnvironment.java:1043) at com.sun.tools.javac.processing.JavacProcessingEnvironment.doProcessing(JavacProcessingEnvironment.java:1184) at com.sun.tools.javac.main.JavaCompiler.processAnnotations(JavaCompiler.java:1170) at com.sun.tools.javac.main.JavaCompiler.processAnnotations(JavaCompiler.java:1068) at org.jetbrains.kotlin.kapt3.base.AnnotationProcessingKt.doAnnotationProcessing(annotationProcessing.kt:79) at org.jetbrains.kotlin.kapt3.base.AnnotationProcessingKt.doAnnotationProcessing$default(annotationProcessing.kt:35) at org.jetbrains.kotlin.kapt3.base.Kapt.kapt(Kapt.kt:45) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:498) at org.jetbrains.kotlin.gradle.internal.KaptExecution.run(KaptWithoutKotlincTask.kt:158) at org.gradle.workers.internal.AdapterWorkAction.execute(AdapterWorkAction.java:57) at org.gradle.workers.internal.DefaultWorkerServer.execute(DefaultWorkerServer.java:63) at org.gradle.workers.internal.NoIsolationWorkerFactory$1$1.create(NoIsolationWorkerFactory.java:67) at org.gradle.workers.internal.NoIsolationWorkerFactory$1$1.create(NoIsolationWorkerFactory.java:63) at org.gradle.internal.classloader.ClassLoaderUtils.executeInClassloader(ClassLoaderUtils.java:97) at org.gradle.workers.internal.NoIsolationWorkerFactory$1.lambda$execute$0(NoIsolationWorkerFactory.java:63) at org.gradle.workers.internal.AbstractWorker$1.call(AbstractWorker.java:44) at org.gradle.workers.internal.AbstractWorker$1.call(AbstractWorker.java:41) at org.gradle.internal.operations.DefaultBuildOperationExecutor$CallableBuildOperationWorker.execute(DefaultBuildOperationExecutor.java:409) at org.gradle.internal.operations.DefaultBuildOperationExecutor$CallableBuildOperationWorker.execute(DefaultBuildOperationExecutor.java:399) at org.gradle.internal.operations.DefaultBuildOperationExecutor$1.execute(DefaultBuildOperationExecutor.java:157) at org.gradle.internal.operations.DefaultBuildOperationExecutor.execute(DefaultBuildOperationExecutor.java:242) at org.gradle.internal.operations.DefaultBuildOperationExecutor.execute(DefaultBuildOperationExecutor.java:150) at org.gradle.internal.operations.DefaultBuildOperationExecutor.call(DefaultBuildOperationExecutor.java:94) at org.gradle.internal.operations.DelegatingBuildOperationExecutor.call(DelegatingBuildOperationExecutor.java:36) at org.gradle.workers.internal.AbstractWorker.executeWrappedInBuildOperation(AbstractWorker.java:41) at org.gradle.workers.internal.NoIsolationWorkerFactory$1.execute(NoIsolationWorkerFactory.java:60) at org.gradle.workers.internal.DefaultWorkerExecutor.lambda$submitWork$2(DefaultWorkerExecutor.java:200) at java.util.concurrent.FutureTask.run(FutureTask.java:266) at org.gradle.internal.work.DefaultConditionalExecutionQueue$ExecutionRunner.runExecution(DefaultConditionalExecutionQueue.java:215) at org.gradle.internal.work.DefaultConditionalExecutionQueue$ExecutionRunner.runBatch(DefaultConditionalExecutionQueue.java:164) at org.gradle.internal.work.DefaultConditionalExecutionQueue$ExecutionRunner.run(DefaultConditionalExecutionQueue.java:131) at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511) at java.util.concurrent.FutureTask.run(FutureTask.java:266) at org.gradle.internal.concurrent.ExecutorPolicy$CatchAndRecordFailures.onExecute(ExecutorPolicy.java:64) at org.gradle.internal.concurrent.ManagedExecutorImpl$1.run(ManagedExecutorImpl.java:48) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) at org.gradle.internal.concurrent.ThreadFactoryImpl$ManagedThreadRunnable.run(ThreadFactoryImpl.java:56) at java.lang.Thread.run(Thread.java:748)
public abstract interface JobPostingMapper {
Hi, thanks a lot for this project!
I have a maven module with data classes all annotated with @KotlinBuilder
and all the builders are successfully created and put into the jar.
I have another maven module that depends on the data classes and it tries to use mapstruct mapping these data classes to something.
During compilation kapt throws an error that "no parameterless constructor exists for type", basically, it doesn't see the builders but it does see the class itself. Not sure why is that, because they are both laying in the same folder in that jar.
May or may not be related to projectlombok/lombok#1538
ex data class in kotlin -
import com.github.pozo.KotlinBuilder
@KotlinBuilder
data class ContactInfo(val phoneNumbers: List<PhoneNumber> = emptyList(),
val emails: List<EmailAddress> = emptyList(),
val addresses: List<PostalAddress> = emptyList()
)
The create function within the generated builder looks like:
public ContactInfo create() { return new ContactInfo(phoneNumbers, emails, addresses); }
The create here uses an all args constructor. However, if the object builders never set a value for one mandatory field (addresses field, for example) the create() call using an all args constructor will try to set addresses to null
, which will throw an NPE type error since this value can't be null in the target kotlin object.
I would like a way for the create() method to:
This would be amazing and very helpful to generate more accurate builders!!
implementation("com.github.pozo:mapstruct-kotlin:1.3.1.1")
implementation("org.mapstruct:mapstruct:1.3.1.Final")
kapt("org.mapstruct:mapstruct-processor:1.3.1.Final")
kapt("com.github.pozo:mapstruct-kotlin-processor:1.3.1.1")
@Entity
class UserEntity(
@Id
var id: Long? = null,
@Enumerated(EnumType.STRING)
val type: ACCOUNT_TYPE)
@KotlinBuilder
data class UserDTO(val id: Long,
val type: ACCOUNT_TYPE)
enum class ACCOUNT_TYPE {
S,
M,
C
}
error in generating builder class
public final class UserDTOBuilder {
public UserDTO create() {
return new UserDTO();
}
public static UserDTOBuilder builder() {
return new UserDTOBuilder();
}
}
DTO without Enum works perfect
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.