Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Not possible to map enum to enum, or data class containing aggregated enums #18

Closed
victormikhailov77 opened this issue Feb 1, 2023 · 6 comments
Labels
bug Something isn't working

Comments

@victormikhailov77
Copy link

victormikhailov77 commented Feb 1, 2023

Im trying to create mapping for class containing enums.
Direct mapping enum to enum doesnt work because : Could not find a no-arg constructor or object supplier for class

I tried with supplier, but supplier takes no arguments - it doesn't make sense because we can't have empty enum , it should be constructed from string parameter

// source enum
@DefaultMappingTarget (Operation.class)
enum class OperationDto { 
   AND, OR, XOR ;

    companion object {
        fun from(value: String) = values().find { it.name.equals(value, ignoreCase = true) }
    }
}

// target enum
enum class Operation { 
    AND, OR, XOR ;

    constructor()

    companion object {
        fun from(value: String) = values().find { it.name.equals(value, ignoreCase = true) }
    }

}

I tried different way - in the outer class which aggregated enum, added custom transformer :

data class OuterClass (
    @MappedField(transformer = OperationTransformer::class)
    val operation: OperationDto
)

class OperationTransformer : MappingTransformer<OperationDto, Operation> {
    override fun transform(context: MappingTransformerContext<out OperationDto>): Operation? {
       return Operation.from(context.originalValue?.toString() ?: "") // maps one enum value to other via string
    }
}

Now I have different error, Could not find transformer by type [class OperationTransformer]

@avivm
Copy link
Contributor

avivm commented Feb 1, 2023

I don't know how you instantiated ShapeShift so it's possible you also did not declare the transformer for ShapeShift, but I see one apparent issue from your code;

You put @DefaultMappingTarget on your enum. It doesn't belong there, instead you should put it on the class where you are declaring the mappings. OuterClass in your case. (Relevant documentation)

In any case, here is a working example with enum mapping, let me know if you have any questions. (Gist)

import dev.krud.shapeshift.ShapeShiftBuilder
import dev.krud.shapeshift.resolver.annotation.DefaultMappingTarget
import dev.krud.shapeshift.resolver.annotation.MappedField
import dev.krud.shapeshift.transformer.base.MappingTransformer
import dev.krud.shapeshift.transformer.base.MappingTransformerContext

enum class OuterOperation { AND, OR, XOR }

enum class InnerOperation { AND, OR, XOR }

@DefaultMappingTarget(InnerClass::class)
data class OuterClass (
    @MappedField(transformer = OperationTransformer::class)
    val operation: OuterOperation
)

data class InnerClass(
    val operation: InnerOperation
)

class OperationTransformer : MappingTransformer<OuterOperation, InnerOperation> {
    override fun transform(context: MappingTransformerContext<out OuterOperation>): InnerOperation? {
        return InnerOperation.valueOf(context.originalValue?.toString() ?: "")
    }
}

fun main() {
    val shapeShift = ShapeShiftBuilder()
        .withTransformer(OperationTransformer())
        .withObjectSupplier { OuterClass(OuterOperation.AND) }
        .withObjectSupplier { InnerClass(InnerOperation.AND) }
        .build()
    val innerClass = shapeShift.map<OuterClass, InnerClass>(OuterClass(OuterOperation.OR))
    println("Operation: ${innerClass.operation}")
}

@victormikhailov77
Copy link
Author

victormikhailov77 commented Feb 1, 2023

thanks for quick response.
yes it works if I add every transformer into the BuilderList
I supposed that if transformer is mentioned in the annotation : @MappedField(transformer = OperationTransformer::class), it is enough.

withObjectsupplier is not needed - works without it

@victormikhailov77
Copy link
Author

Hi, can you please give me a clue, why collection transformation of aggregated type doesn't work?
The mapping goes like this: FilterRequestDto -> FilterRequest, FilterDto - > Filter

@DefaultMappingTarget(FilterRequest::class)
data class FilterRequestDto(
    @MappedField
    val layers: List<String> = emptyList(),

    @MappedField(transformer = ImplicitCollectionMappingTransformer::class)
    val filters: List<FilterDto> = emptyList(), // corresponds to List<Filter> in other class
)

the aggregated type in the collection also has it's own mapping

@DefaultMappingTarget(Filter::class)
data class FilterDto(
    @MappedField
    val layerId: String? = null
   /// .......
)

when I do the converstion:

        val shapeShift = ShapeShiftBuilder()
            .withTransformer(ImplicitMappingTransformer())
            .withTransformer(ImplicitCollectionMappingTransformer())
            .build()
        val obj: FilterRequest = shapeShift.map(requestDto)

the collection in the target object has elements of type "sun.reflect.generics.reflectiveObjects.ParameterizedTypeImpl", instead of Filter.
Should be mapped from List to List

without list, if I use it as scalar type, the transformation works.

Am I missing something?

@Idane
Copy link
Member

Idane commented Feb 2, 2023

@victormikhailov77
Seems to have been a bug with the transformer, please try with 0.8.0.
Also, shouldn't need to declare it anymore as it's a default transformer (another bug)

@Idane Idane added the bug Something isn't working label Feb 2, 2023
@victormikhailov77
Copy link
Author

Yes, the bug fixed, confirmed.
Thanks a lot! Your level of support is the best.
Im migrating project from mapstruct-kotlin to ShapeShift, because mapstruct-kotlin is abandoned, and not supported in future Kotlin versions.

@avivm
Copy link
Contributor

avivm commented Feb 2, 2023

Good luck! We hope ShapeShift will serve you well. If you find any bugs or think of additional features you would like let us know.

@avivm avivm closed this as completed Feb 2, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

3 participants