Search code examples
javamappingdtoorika

Orika optional conversion/mapping in runtime


I'm using Orika mapper to avoid boilerplate converters and have an interesting problem. To simplify development, I need to create an Annotation that will determine when to map the value and when to leave it as is (not null or default, just as is).

Imagine that we have 2 classes:

class Dto {
    public int id;
    public String name;
}

class Entity {
    public int id;
    public String name;
}

Dto comes from Front-end and we want to map the field name to an entity only if it starts with something like:

if {dto.name.startsWith("A")}
    entity.name = dto.name;

And we need similar logic in a lot of classes - only starting string is changed.

So I want to create annotation @IfStartsWith(String startsWith) and do following:

class Entity{
    public int id;
    @IfStartsWith("A")
    public String name;
}

On project startup I want to config Orika mapper reading this annotation from needed classes and setup dynamically mapping behavior depending on this.

The important thing here is that I can't use default (null or empty string) because I need to leave destination value as is if check fails. "As is" means either value that is in existing destination object or class default value (set as public String name = "My name";)

I know how to get annotations from fields and it works now but the questions are:

What should I use: field-level converter or class-level customization?

Is there any way to avoid reflection in field value setting inside these customized converters/mappers?


Solution

  • Ok, so I've managed to solve this (not saying it's pretty).

    Basically, I found that Orika can't "leave" field as is. If you actually do a mapping from field to field then the value will be set whatever it is.

    I read all fields using reflection and do following logic:

    if field has no @IfStartsWith annotation 
    then it's mapped as usual:
       classMapBuilder.fieldAToB(*).fieldBToA(*)
    if field has custom annotation 
    then I create a CustomMapper object that handles the logic.
    

    Please note that this is a class-level custom mapper.

    Important thing here is that you DON'T do standard mapping (fieldAToB, default and others). Before custom mapper Orika doesn't even know that there is such a field and it has to be mapped. Custom mapper uses reflection (because I don't know in advance which field will be mapped this way).

    Hope this will help somebody.