3

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
    Your use-case without Spock doesn't test the same thing, in Spock you test `TestClassDto` ->`TestClass` in the other case you test `TestClass` -> `TestClass`. If you change it to `TestClassDto test = new TestClassDto(fieldA: 'anyA', fieldB: 'anyB')` it will fail with the same error. – Leonard Brünings Oct 28 '21 at 20:49
  • Thanks, I'll edit the question to reflect that. – Emily Johnson Oct 29 '21 at 13:46
  • Not a solution, but a workaround. If you annotate your mapping code/function with `@groovy.transform.CompileStatic` it doesn't fail anymore. And if you use the getter instead of the property you get a different error that might point at the culprit `java.lang.ClassCastException: class TestClass cannot be cast to class TestClassDto (TestClass and TestClassDto are in unnamed module of loader groovy.lang.GroovyClassLoader$InnerLoader`. Anyway this looks like a potential bug in Groovy, so you might create an issue here https://issues.apache.org/jira/projects/GROOVY/issues – Leonard Brünings Oct 29 '21 at 23:55
  • I'm experiencing the same problem and getting the exact same error message - Groovy 3.0.7 // ModelMapper 3.1.0. I tried annotating with @CompileStatic but it doesn't fix the problem. Did you happen to find a solution, @EmilyJohnson? – zniwalla Mar 18 '22 at 08:51
  • This is likely due to the fact that Modelmapper doesn't set the metaClass properly. You can workaround this by doing the following: `A a; B b = mapper.map(a, B);` `b.metaClass = B.metaClass //this is the crucial part` – h8red Jul 14 '22 at 09:42

1 Answers1

0

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)
h8red
  • 713
  • 4
  • 17