I'm trying to configure ModelMapper to use a certain converter for all properties that implement a certain interface, across any entity class. I can get everything working by defining a TypeMap for each entity, and defining mappings for each relevant property, but I'm hoping there's a simpler way that doesn't need to be updated anytime a property is added.
So far I have something like this:
public interface Labelled { String getLabel; // returns a message code }
public class Vegetable implements labelled { ... }
public class Protein implements labelled { ... }
public class Starch implements labelled { ... }
public class Dinner { Vegetable veg; Starch starch; Protein protein; String name; // getters and setters }
public class DinnerDto { String veg; String starch; String protein; String name; // get/set }
public class LabelledToStringConverter extends AbstractConverter<Labelled, String> {
private final MessageSource messageSource;
public LabelledToStringConverter(MessageSource messageSource) {
this.messageSource = messageSource;
}
@Override
protected String convert(Labelled labelled) {
final Locale locale = LocaleContextHolder.getLocale();
final String messageCode = labelled.getLabel();
return messageSource.getMessage(messageCode, null, messageCode, locale);
}
}
@Configuration
public class Configuration {
@Bean
public ModelMapper modelMapper(final MessageSource messageSource) {
final ModelMapper mapper = new ModelMapper();
final Converter<Labelled, String> converter = new LabelledToStringConverter(messageSource);
mapper.createTypeMap(Dinner.class, DinnerDto.class)
.addMappings(map -> {
map.using(converter).map(Dinner::getVegetable, DinnerDto::setVegetable);
map.using(converter).map(Dinner::getProtein, DinnerDto::setProtein);
map.using(converter).map(Dinner::getStarch, DinnerDto::setStarch);
});
return mapper;
}
...but I'd prefer to avoid mapping each individual field, and if possible each type. TypeMap.setPropertyConverter doesn't work when there are fields of other types. ModelMap.addConverter doesn't appear to do anything (I've tried both implementing Converter and extending AbstractConverter).
Is there another way to configure ModelMapper with my converter and have it apply that to all relevant fields without explicitly defining a mapping for each one?
Rather than calling
ModelMapper.addConverter, which actually creates or adds to aTypeMapunder the hood, a converter can be added to ModelMapper's implicit mapping by implementingConditionalConverterand adding our converter directly to the underlying configuration. The converter class:The
ModelMappersetup:This will use the converter implementation anytime the conditions are matched, here anytime the model mapper is converting a property from
LabelledtoString.