Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ModelMapper: mapping abstract classes during runtime

I am using ModelMapper Framework (http://modelmapper.org/) for mapping objects in Java. I have encountered a problem while mapping concrete classes (DTO to Entites) containing abstract classes.

Example: Task has a list of AbstractItems.
AbstractItems are Question and Criteria.

public class TaskDTO {
   ...
   private List<AbstractItemDTO> items;
}

Mapping method:

// task is an TaskDTO object
return getModelMapper().map(task, TaskEntity.class);

ModelMapper tries to create a new instance of AbstractItem, which throws an exception. Is there a way to map the abstract classes during the runtime?
Like QuestionDTO -> Question, CriteriaDTO ->Criteria

like image 217
kk-dev11 Avatar asked Jan 23 '15 14:01

kk-dev11


2 Answers

I also had this problem and solved this with:

public void configure(ModelMapper modelMapper) {
    modelMapper.typeMap(QuestionDto.class, AbstractItem.class)
            .setConverter(converterWithDestinationSupplier(Question::new));
    modelMapper.typeMap(CriteriaDto.class, AbstractItem.class)
            .setConverter(converterWithDestinationSupplier(Criteria::new));
}

private <S, D> Converter<S, D> converterWithDestinationSupplier(Supplier<? extends D> supplier ) {
    return ctx -> ctx.getMappingEngine().map(ctx.create(ctx.getSource(), supplier.get()));
}

Converter uses supplier to create required instance and then uses right typeMap (QuestionDto -> Question or CriteriaDto -> Criteria) to map all properties.

like image 193
eldar Avatar answered Nov 09 '22 09:11

eldar


Where a kotlin solution with ModelMapper

DTOs (aka source class)

abstract class BaseRequirementDTO()
class AttributeRequirementDTO(

        var attribute: Attribute = Attribute.STRENGTH,
        var attributeValue: Long = 10

) : BaseRequirementDTO()
class LevelRequirementDTO(

        var requiredLevel: Long = 1

) : BaseRequirementDTO()

My Models (aka destination class)

abstract class BaseRequirement() : Model()
class AttributeRequirement(

        var attribute: Attribute = Attribute.STRENGTH,
        var attributeValue: Long = 10

) : BaseRequirement()
class LevelRequirement(

        var requiredLevel: Long = 1

) : BaseRequirement()

Mapper configuration

val mapper = ModelMapper()
mapper.configuration.matchingStrategy = MatchingStrategies.STRICT
mapper.createTypeMap(BaseRequirement::class.java, BaseRequirementDTO::class.java)
mapper.typeMap(AttributeRequirementDTO::class.java, BaseRequirement::class.java)
        .setProvider { request -> mapper.map(request.source, AttributeRequirement::class.java) }
mapper.typeMap(LevelRequirementDTO::class.java, BaseRequirement::class.java)
        .setProvider { request -> mapper.map(request.source, LevelRequirement::class.java) }

ModelMapper version: 2.3.7

https://github.com/modelmapper/modelmapper/issues/182#issuecomment-660965119

like image 42
Gabriel Brito Avatar answered Nov 09 '22 09:11

Gabriel Brito