mcarleio / konvert

This kotlin compiler plugin is using KSP API and generates kotlin code to map one class to another
https://mcarleio.github.io/konvert/
Apache License 2.0
93 stars 8 forks source link

Mapping List of objects ksp issue #20

Closed mkowol-n closed 1 year ago

mkowol-n commented 1 year ago

Hi, I'm trying to map list of object and i'm getting an error.

[ksp] io.mcarle.konvert.processor.exceptions.NoMatchingConstructorException: No constructor for List matching the available properties [size] found at io.mcarle.konvert.processor.codegen.ConstructorResolver.determineConstructor(ConstructorResolver.kt:43) at io.mcarle.konvert.processor.codegen.CodeGenerator.generateCode(CodeGenerator.kt:40) at io.mcarle.konvert.processor.konvert.KonverterCodeGenerator$generate$2$1.invoke(KonverterCodeGenerator.kt:85) at io.mcarle.konvert.processor.konvert.KonverterCodeGenerator$generate$2$1.invoke(KonverterCodeGenerator.kt:41) at io.mcarle.konvert.converter.api.config.ConfigurationKt.withIsolatedConfiguration(Configuration.kt:47) at io.mcarle.konvert.processor.konvert.KonverterCodeGenerator.generate(KonverterCodeGenerator.kt:41) at io.mcarle.konvert.processor.KonvertProcessor$generateMappingCode$1$1.invoke(KonvertProcessor.kt:72) at io.mcarle.konvert.processor.KonvertProcessor$generateMappingCode$1$1.invoke(KonvertProcessor.kt:68) at io.mcarle.konvert.converter.api.config.ConfigurationKt.withIsolatedConfiguration(Configuration.kt:47) at io.mcarle.konvert.processor.KonvertProcessor.generateMappingCode(KonvertProcessor.kt:68) at io.mcarle.konvert.processor.KonvertProcessor.access$generateMappingCode(KonvertProcessor.kt:24) at io.mcarle.konvert.processor.KonvertProcessor$process$1.invoke(KonvertProcessor.kt:42) at io.mcarle.konvert.processor.KonvertProcessor$process$1.invoke(KonvertProcessor.kt:33) at io.mcarle.konvert.converter.api.config.ConfigurationKt.withIsolatedConfiguration(Configuration.kt:41) at io.mcarle.konvert.processor.KonvertProcessor.process(KonvertProcessor.kt:33) at com.google.devtools.ksp.AbstractKotlinSymbolProcessingExtension$doAnalysis$8$1.invoke(KotlinSymbolProcessingExtension.kt:305) at com.google.devtools.ksp.AbstractKotlinSymbolProcessingExtension$doAnalysis$8$1.invoke(KotlinSymbolProcessingExtension.kt:303) at com.google.devtools.ksp.AbstractKotlinSymbolProcessingExtension.handleException(KotlinSymbolProcessingExtension.kt:409) at com.google.devtools.ksp.AbstractKotlinSymbolProcessingExtension.doAnalysis(KotlinSymbolProcessingExtension.kt:303) at org.jetbrains.kotlin.cli.jvm.compiler.TopDownAnalyzerFacadeForJVM.analyzeFilesWithJavaIntegration(TopDownAnalyzerFacadeForJVM.kt:112) at org.jetbrains.kotlin.cli.jvm.compiler.TopDownAnalyzerFacadeForJVM.analyzeFilesWithJavaIntegration$default(TopDownAnalyzerFacadeForJVM.kt:88) at org.jetbrains.kotlin.cli.jvm.compiler.KotlinToJVMBytecodeCompiler$analyze$1.invoke(KotlinToJVMBytecodeCompiler.kt:256) at org.jetbrains.kotlin.cli.jvm.compiler.KotlinToJVMBytecodeCompiler$analyze$1.invoke(KotlinToJVMBytecodeCompiler.kt:42) at org.jetbrains.kotlin.cli.common.messages.AnalyzerWithCompilerReport.analyzeAndReport(AnalyzerWithCompilerReport.kt:115) at org.jetbrains.kotlin.cli.jvm.compiler.KotlinToJVMBytecodeCompiler.analyze(KotlinToJVMBytecodeCompiler.kt:247) at org.jetbrains.kotlin.cli.jvm.compiler.KotlinToJVMBytecodeCompiler.compileModules$cli(KotlinToJVMBytecodeCompiler.kt:87) at org.jetbrains.kotlin.cli.jvm.compiler.KotlinToJVMBytecodeCompiler.compileModules$cli$default(KotlinToJVMBytecodeCompiler.kt:47) at org.jetbrains.kotlin.cli.jvm.K2JVMCompiler.doExecute(K2JVMCompiler.kt:168) at org.jetbrains.kotlin.cli.jvm.K2JVMCompiler.doExecute(K2JVMCompiler.kt:53) at org.jetbrains.kotlin.cli.common.CLICompiler.execImpl(CLICompiler.kt:100) at org.jetbrains.kotlin.cli.common.CLICompiler.execImpl(CLICompiler.kt:46) at org.jetbrains.kotlin.cli.common.CLITool.exec(CLITool.kt:101) at org.jetbrains.kotlin.daemon.CompileServiceImpl.compile(CompileServiceImpl.kt:1497) at jdk.internal.reflect.GeneratedMethodAccessor33.invoke(Unknown Source) at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source) at java.base/java.lang.reflect.Method.invoke(Unknown Source) at java.rmi/sun.rmi.server.UnicastServerRef.dispatch(Unknown Source) at java.rmi/sun.rmi.transport.Transport$1.run(Unknown Source) at java.rmi/sun.rmi.transport.Transport$1.run(Unknown Source) at java.base/java.security.AccessController.doPrivileged(Unknown Source) at java.rmi/sun.rmi.transport.Transport.serviceCall(Unknown Source) at java.rmi/sun.rmi.transport.tcp.TCPTransport.handleMessages(Unknown Source) at java.rmi/sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run0(Unknown Source) at java.rmi/sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.lambda$run$0(Unknown Source) at java.base/java.security.AccessController.doPrivileged(Unknown Source) at java.rmi/sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run(Unknown Source) at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source) at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source) at java.base/java.lang.Thread.run(Unknown Source)

Model I'm trying to map:

data class SomeClassTwo(
    val valueOne: Boolean?,
    val valueTwo: Boolean?
)

to:

@JsonClass(generateAdapter = true)
data class SomeClassOne (

    @Json(name = "valueOne")
    val valueOne: Boolean? = null,

    @Json(name = "valueTwo")
    val valueTwo: Boolean? = null

)

I'm using interface with @Konverter annotation:

fun toDto(
        domain: SomeClassTwo
    ): SomeClassOne
    fun toDto(
        domain: List<SomeClassTwo>
    ): List<SomeClassOne>

Thanks for help!

jakoss commented 1 year ago

I guess the question is - is the iterable mapping available as a method in @Konverter interface or does it work only for lists inside the mapped objects themselves?

mcarleio commented 1 year ago

Konvert can map Iterables, but not in the @Konverter itself.

You can workaround this in several ways for now:

  1. In general I would recommend to have a (simple) domain class to wrap a list of other domain classes, as that typically has its own name. In your code example this would look a bit strange, so I will give a better understandable example:
data class Match(val homeTeam: String, val guestTeam: String)
data class MatchDto(val homeTeam: String, val guestTeam: String)

data class MatchPlan(val matches: List<Match>)
data class MatchPlanDto(val matches: List<MatchDto>)

@Konverter
interface Mapper {
    fun toDto(domain: Match): MatchDto
    fun toDto(domain: MatchPlan): MatchPlanDto
}
  1. probably easiest is to just implement the list mapping yourself:
@Konverter
interface Mapper {
    fun toDto(domain: SomeClassTwo): SomeClassOne
    fun toDto(domain: List<SomeClassTwo>) = domain.map { toDto(it) }
}
jakoss commented 1 year ago

Oh, that makes sense. It would be nice feature for the future releases though :)

mcarleio commented 1 year ago

I implemented this, so the next version of Konvert will work as expected :+1:

mcarleio commented 1 year ago

Released with https://github.com/mcarleio/konvert/releases/tag/v2.3.0 Please try it out and report back if anything is not working as expected