Search code examples
javaorika

Orika: java.lang.ClassCastException: java.lang.Object cannot be cast to mapper.Name


I'm doing sample example orika mapper documentation in here section Mapping elements of Arrays and Lists.

Below is my code to create mapperFacade and convert Person object to PersonDto -

Name class definition -

    public class Name {
        private String first;
        private String last;
        private String fullName;
        // getters/setters

        public Name() {

        }

        public Name(String first, String last, String fullName) {
            this.first = first;
            this.last = last;
            this.fullName = fullName;
        }

        public String getFirst() {
            return first;
        }

        public void setFirst(String first) {
            this.first = first;
        }

        public String getLast() {
            return last;
        }

        public void setLast(String last) {
            this.last = last;
        }

        public String getFullName() {
            return fullName;
        }

        public void setFullName(String fullName) {
            this.fullName = fullName;
        }
    }

Person class definition -

import java.util.List;

public class Person {
 private List<Name> names;
 // getters/setters


 public Person() {
 }

public List<Name> getNames() {
    return names;
}

 public void setNames(List<Name> names) {
    this.names = names;
 }
}

PersonDto class definition -

import java.util.List;
import java.util.Map;

public class PersonDto {
    private Map<String, Name> personalNames;
    private String[] firstNames;
    private List<String> lastNames;
    // getters/setters omitted

    public PersonDto() {
    }

    public Map<String, Name> getPersonalNames() {
        return personalNames;
    }

    public void setPersonalNames(Map<String, Name> personalNames) {
        this.personalNames = personalNames;
    }

    public String[] getFirstNames() {
        return firstNames;
    }

    public void setFirstNames(String[] firstNames) {
        this.firstNames = firstNames;
    }

    public List<String> getLastNames() {
        return lastNames;
    }

    public void setLastNames(List<String> lastNames) {
        this.lastNames = lastNames;
    }
}


    Map<String,String> fieldMap = new HashMap<>();
    fieldMap.put("names{fullName}", "personalNames{key}");
    fieldMap.put("names{}", "personalNames{value}");
    MapperFactory mapperFactory = new DefaultMapperFactory.Builder().mapNulls(false).dumpStateOnException(false).build();
    ClassMapBuilder<Person, PersonDto> classBuilder = mapperFactory.classMap(Person.class, PersonDto.class);
    fieldMap.forEach((k,v) -> classBuilder.field(k,v));
    classBuilder.register();
    BoundMapperFacade<Person, PersonDto> delegate = mapperFactory.getMapperFacade(Person.class, PersonDto.class);



    Person person = new Person();
    Name n1 = new Name("raj", "kumar", "raj kumar");
    Name n2 = new Name("senthil", "kumar", "senthil kumar");
    person.setNames(Arrays.asList(n1, n2));
    PersonDto pDto = mapper.map(person);
    System.out.println(pDto);

When I run this code, I get below error -

java.lang.Object cannot be cast to mapper.Name java.lang.ClassCastException: java.lang.Object cannot be cast to mapper.Name at ma.glasnost.orika.generated.Orika_PersonDto_Person_Mapper12572858142441$0.mapAtoB(Orika_PersonDto_Person_Mapper12572858142441$0.java) at ma.glasnost.orika.impl.mapping.strategy.UseCustomMapperStrategy.map(UseCustomMapperStrategy.java:77) at ma.glasnost.orika.impl.DefaultBoundMapperFacade.map(DefaultBoundMapperFacade.java:137) at ma.glasnost.orika.impl.DefaultBoundMapperFacade.map(DefaultBoundMapperFacade.java:94) at mapper.PersonToPersonDtoMapper.map(PersonToPersonDtoMapper.java:29) at mapper.PersonToPersonDtoMapper.map(PersonToPersonDtoMapper.java:14) at mapper.PersonToPersonDtoMapperTest.test1(PersonToPersonDtoMapperTest.java:29) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:498) at org.testng.internal.MethodInvocationHelper.invokeMethod(MethodInvocationHelper.java:133) at org.testng.internal.TestInvoker.invokeMethod(TestInvoker.java:584) at org.testng.internal.TestInvoker.invokeTestMethod(TestInvoker.java:172) at org.testng.internal.MethodRunner.runInSequence(MethodRunner.java:46) at org.testng.internal.TestInvoker$MethodInvocationAgent.invoke(TestInvoker.java:804) at org.testng.internal.TestInvoker.invokeTestMethods(TestInvoker.java:145) at org.testng.internal.TestMethodWorker.invokeTestMethods(TestMethodWorker.java:146) at org.testng.internal.TestMethodWorker.run(TestMethodWorker.java:128) at java.util.ArrayList.forEach(ArrayList.java:1249) at org.testng.TestRunner.privateRun(TestRunner.java:770) at org.testng.TestRunner.run(TestRunner.java:591) at org.testng.SuiteRunner.runTest(SuiteRunner.java:402) at org.testng.SuiteRunner.runSequentially(SuiteRunner.java:396) at org.testng.SuiteRunner.privateRun(SuiteRunner.java:355) at org.testng.SuiteRunner.run(SuiteRunner.java:304)

I have this code hosted on github here.

What is wrong with my code? How can I fix this issue?


Solution

  • The issue happens because Java Generics are stripped out in runtime. Map.Entry<String, Name> (compile time) becomes Map.Entry<Object, Object> (runtime).

    You can overcome the problem by adjusting the ClassMapBuilder as follows:

    Map<String, String> fieldMap = new HashMap<>();
    fieldMap.put("names{fullName}", "personalNames{key}");
    fieldMap.put("names{}", "personalNames{value}");
    MapperFactory mapperFactory = new DefaultMapperFactory.Builder().mapNulls(false).dumpStateOnException(false).build();
    // Swap Person, PersonDto generic arguments.
    ClassMapBuilder<PersonDto, Person> classBuilder = mapperFactory.classMap(PersonDto.class, Person.class);
    fieldMap.forEach((k, v) -> classBuilder.field(v, k));
    classBuilder.register();
    // rest of the code is the same