ModelMapper 2.4.4 and Groovy 3.0 compatibility issue

Viewed 287

When switching from Groovy 2 to Groovy 3, ModelMapper 2.4.4 seems to now be failing to convert objects. ModelMapper itself does not throw an error, but rather just returns an object whose metaClass is still the initial class rather than the new post-conversion class.

This is demonstrated in the below code which, when run with Groovy 3 (tested with 3.0.2 and 3.0.9), then throws java.lang.IllegalArgumentException: object is not an instance of declaring class when accessing any of the properties of the returned object post-ModelMapping. This error does not happen when run in Groovy 2 (2.5.15).

Dependencies:

org.modelmapper:modelmapper:2.4.4
org.codehaus.groovy:groovy:3.0.9
import org.modelmapper.ModelMapper

class TestClass {

    String fieldA
    String fieldB
}

class TestClassDto {
    String fieldA
    String fieldB
}

TestClassDto test = new TestClassDto(fieldA: 'anyA', fieldB: 'anyB')
System.out.println(new ModelMapper().map(test, TestClass).fieldA)
1 Answers

The issue is in the fact that metaClass gets automatically mapped (so, testclass.metaClass gets replaces with TestClass.metaClass and groovy considers the final object to be an instance of TestClass). There are multiple ways to fix this.

You can explicitly set the metaclass after the mapping has been done:

def 'testMapping'() {
        given:
        TestClassDto test = new TestClassDto(fieldA: 'anyA', fieldB: 'anyB')
        def mapped = new ModelMapper().map(test, TestClass)
        mapped.metaClass = TestClass.metaClass

        expect:
        mapped.fieldA == 'anyA'
}

Alternatively, use @CompileStatic so that metaclass isn't generated at all.

Or you can even configure Modelmapper to skip metaclass:

mapper.typeMap(TestClassDto, TestClass)
                .addMappings({ it.skip(GroovyObject::setMetaClass) } as ExpressionMap)
Related