Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Modelmapper: How to apply custom mapping when source object is null?

Lets assume I have class MySource:

public class MySource {
    public String fieldA;
    public String fieldB;

    public MySource(String A, String B) {
       this.fieldA = A;
       this.fieldB = B;
    }
}

and I would like to translate it to object MyTarget:

public class MyTarget {
    public String fieldA;
    public String fieldB;
}

using default ModelMapper settings I can achieve it in the following way:

ModelMapper modelMapper = new ModelMapper();
MySource src = new MySource("A field", "B field");
MyTarget trg = modelMapper.map(src, MyTarget.class); //success! fields are copied

It can happen however, that MySource object will be null. In this case, MyTarget will be also null:

    ModelMapper modelMapper = new ModelMapper();
    MySource src = null;
    MyTarget trg = modelMapper.map(src, MyTarget.class); //trg = null

I would like to specify custom mapping in such a way, that (pseudo code):

MySource src != null ? [perform default mapping] : [return new MyTarget()]

Does anybody have idea how to write an appropriate converter to achieve that?

like image 488
Tinki Avatar asked Jun 22 '16 14:06

Tinki


People also ask

How do you use PropertyMap?

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. The solution for that problem is by using Converter .

Does ModelMapper use reflection?

I believe that ModelMapper is based on reflection and performs the mapping during runtime. Whereas MapStruct is a code generator which generates the mapping code (java classes) during compilation time. So naturally if you are worried about performance then MapStruct is the clear choice.

How does ModelMapper work in spring boot?

ModelMapper consists of two separate processes: the matching process, where a source and destination type's properties are matched to each other, and the mapping process where matched property values are converted from a source to destination object.

Can we Autowire ModelMapper?

When we use the starter, we just add a Gradle dependency and then autowire the ModelMapper instance in our controllers. @Controller public class TaskController { @Autowired private TaskRepository taskRepository; @Autowired private ModelMapper modelMapper; ... }


1 Answers

It is not possible using ModelMapper directly because ModelMapper map(Source, Destination) method checks if source is null, in that case it throws an exception...

Take a look to ModelMapper Map method implementation:

public <D> D map(Object source, Class<D> destinationType) {
    Assert.notNull(source, "source"); -> //IllegalArgument Exception
    Assert.notNull(destinationType, "destinationType");
    return mapInternal(source, null, destinationType, null);
  }

Solution

I propose extends ModelMapper class and override map(Object source, Class<D> destinationType) like this:

public class MyCustomizedMapper extends ModelMapper{

    @Override
    public <D> D map(Object source, Class<D> destinationType) {
        Object tmpSource = source;
        if(source == null){
            tmpSource = new Object();
        }

        return super.map(tmpSource, destinationType);
    }
}

It checks if source is null, in that case it initializes then it calls super map(Object source, Class<D> destinationType).

Finally, you could use your customized mapper like this:

public static void main(String args[]){
    //Your customized mapper
    ModelMapper modelMapper = new MyCustomizedMapper();

    MySource src = null;
    MyTarget trg = modelMapper.map(src, MyTarget.class); //trg = null
    System.out.println(trg);
}

The output would be a new MyTarget():

Output Console: NullExampleMain.MyTarget(fieldA=null, fieldB=null)

Thus it is initialized.

like image 142
Pau Avatar answered Oct 22 '22 02:10

Pau