Java - Using ModelMapper with Custom PropertyMap & Converter

ModelMapper is a popular Java library by which you can map an instance of a class to an instance of another class. By default, it maps properties with the same name. The challange is how to make the mapper maps properties whose name in source and destination is different and how to perform some operations to return customized value. For example we have two classes Person and User and we're going to map an object from Person to User.

  class Person {
    String firstName;
    String familyName;
    int age;
  }
  class User {
    String firstName;
    String lastName;
    String ageStr;
  }

Let's assume all getters and setters for each property have been created. The firstName property is present on both classes, so we don't need to do anything for that property as it will be automatically mapped by ModelMapper, unless we want the mapped value modified. The familyName property on Person class will be mapped to lastName property on the User class. To do so, create a new PropertyMap<Person, User>. Inside, implement configure method. For each property you want to map, call map(), which is of type User (the destination class), then call the setter setLastName with source.getFamilyName() as the argument. Then, add the PropertyMap mapping to the ModelMapper instance.

  PropertyMap<Person, User> personMap = new PropertyMap <Person, User>() {
      protected void configure() {
          map().setLastName(source.getFamilyName());
      }
  };

  modelMapper.addMappings(personMap);

What you can do inside PropertyMap is very restricted as it uses Embedded Domain Specific Language (EDSL) to define how the values should be mapped from source to destination. Therefore you can't do many things, even using simple loop or branching may cause the following error:

ModelMapper: Ensure that method has zero parameters and does not return void

The solution for that problem is by using Converter. The converter below formats the output to be in uppercase.

  Converter<String, String> toUppercase = new 
  AbstractConverter<String, String>() {
      protected String convert(String source) {
          return source == null ? null : source.toUppercase();
      }
  };

Below is the example how to use the toUppercase function.

 
 using(toUppercase).map().setLastName(source.getFamilyName());

If you're using Java 8 or above, you can use ExpressionMapping

  modelMapper.addMapping(Person::getFamilyName, User::setLastName);

It also works if the source and destination type is different.

  modelMapper.addMapping(Person::getAge, User::setAgeStr);

And here's how to use converter.

  Converter<String, String> toUppercase =
    context -> context.getSource() == null ? null : context.getSource().toUppercase();

  modelMapper.addMappings(mapper -> mapper.using(toUppercase).map(Person::getFamilyName, User::setLastName));

Alternatively, you can use lambda expression.

  modelMapper.addMappings(mapper -> mapper.using(context -> (context.getSource()).toUpperCase())
	.map(Person::getFamilyName, User::setLastName));