I have the following class hierarhy:
public abstract class Base {
protected Boolean baseBoolean;
}
public class A extends Base {
private BigDecimal amount;
}
And trying to map DTO to entity
public class DTO {
private Base details;
}
public class Entity {
private Base details;
}
And map as follows:
public static void main(String[] args) {
ModelMapper modelMapper = new ModelMapper();
modelMapper.getConfiguration().setDeepCopyEnabled(true);
A a = new A();
a.setAmount(BigDecimal.ONE);
a.setBaseBoolean(true);
DTO request = DTO.builder().base(a).build();
Entity entity = modelMapper.map(request, Entity.class);
System.out.println(entity);
}
I recieve DTO with A or B in details field, this is checked wiht debugger. But modelmapper throws
Failed to instantiate instance of destination org.package.Base. Ensure that org.package.Base has a non-private no-argument constructor.
I tried to use explicit Provider (which was not used for this mappping):
modelMapper.typeMap(A.class, Base.class).setProvider(new Provider<Base>() {
@Override
public Base get(ProvisionRequest<Base> r) {
return new A();
}
});
I also tried to implement custom converter like this (which did not execute either):
modelMapper.typeMap(A.class, Base.class).setConverter(new Converter<A, Base>() {
@Override
public Base convert(MappingContext<A, Base> mappingContext) {
return modelMapper.map(mappingContext.getSource(), A.class);
}
});
It seems that modelmapper don't use this typemaps for fields, only for root of hierarhy. How can I map class hierarhies in case like this?
As you have found, the problem occurs when deep copy is enabled: modelMapper.getConfiguration().setDeepCopyEnabled(true)
.
A solution is to define a Converter<Base, Base>
like follows:
ModelMapper modelMapper = new ModelMapper();
modelMapper.getConfiguration().setDeepCopyEnabled(true); // this triggers the problem
// the converter resolves it
Converter<Base, Base> baseBaseConverter = context ->
modelMapper.map(context.getSource(), context.getSource().getClass());
modelMapper.createTypeMap(Base.class, Base.class).setConverter(baseBaseConverter);
Here is a more detailed post on the topic.