'MapStruct with groovy classes

I want to use the Mapstruct mapper on groovy classes with gradle. The configuration in the build.gradle looks like in a Java project.

dependencies {
    ...
    compile 'org.mapstruct:mapstruct:1.4.2.Final'

    annotationProcessor 'org.mapstruct:mapstruct-processor:1.4.2.Final'
    testAnnotationProcessor 'org.mapstruct:mapstruct-processor:1.4.2.Final' // if you are using mapstruct in test code
}

The problem is that the implementation classes for the mappers are not generated. I tried also to apply different options for the groovy compile task, but it doesn't work.

compileGroovy {
    options.annotationProcessorPath = configurations.annotationProcessor
    // if you need to configure mapstruct component model
    options.compilerArgs << "-Amapstruct.defaultComponentModel=default"
    options.setAnnotationProcessorGeneratedSourcesDirectory( file("$projectDir/src/main/generated/groovy"))
}

Does anyone know if Mapstruct can work together with groovy classes and how I have to configure it?



Solution 1:[1]

So you can use this build:

plugins {
    id 'groovy'
}

repositories {
    mavenCentral()
}

dependencies {
    implementation 'org.mapstruct:mapstruct:1.4.2.Final'
    implementation 'org.codehaus.groovy:groovy-all:3.0.9'
    annotationProcessor 'org.mapstruct:mapstruct-processor:1.4.2.Final'
}

compileGroovy.groovyOptions.javaAnnotationProcessing = true

With this Car (slightly modified from their example)

import groovy.transform.Immutable

@Immutable
class Car {
    String make;
    int numberOfSeats;
    CarType type;
}

And this CarDto (again, slightly modified)

import groovy.transform.ToString

@ToString
class CarDto {

    String make;
    int seatCount;
    String type;
}

Then the only change you need to make in the CarMapper is to ignore the metaClass property that Groovy adds to objects:

import org.mapstruct.Mapper
import org.mapstruct.Mapping
import org.mapstruct.factory.Mappers

@Mapper
interface CarMapper {
    CarMapper INSTANCE = Mappers.getMapper(CarMapper)

    @Mapping(source = "numberOfSeats", target = "seatCount")
    @Mapping(target = "metaClass", ignore = true)
    CarDto carToCarDto(Car car)
}

And then you can do this:

class Main {

    static main(args) {
        Car car = new Car("Morris", 5, CarType.SEDAN);

        CarDto carDto = CarMapper.INSTANCE.carToCarDto(car);

        println carDto
    }
}

Which prints out:

main.CarDto(Morris, 5, SEDAN)

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 tim_yates