Home > Back-end >  MapStruct: custom mapping function is applied to all fields
MapStruct: custom mapping function is applied to all fields

Time:09-12

I have custom mapping function that maps int to String, but I only want it to be applied in one special case. However, it is applied to all source-fields of type int, not just the one annotated with java(...).

Source class:

class Source {
    private int a;
    private int b;
    // getters etc...
}

Target class:

class Target {
    private String a;
    private String b;
    // getters etc...
}

Mapper:

@Mapping(source="a", target="a") // should not be necessary, but to make it more explicit
@Mapping(target="b", expression = "java(modify(b))")
public abstract Target sourceToTarget(Source source);

String modify(int value) {
    return "prefix_"   value;
}

What I want to achieve:

target.setA(String.valueOf(a));
target.setB(modify(b));

However, the generated code does this:

target.setA(modify(a));
target.setB(modify(b));

When removing the expression and modify, MapStruct uses String.valueOf for both values. I tried it with both MapStruct 1.4.2.FINAL as well as 1.5.2.FINAL. Both classes make use of Lombok, however, this hasn't been a problem in the past.

Is this behavior expected? If yes, how else can I make it work?

CodePudding user response:

There are two ways in which you can try :

--> @Mapping#expression Attribute :

@Mapper(componentModel = "spring")
public interface ExampleMapper {

    @Mapping(target = "b", expression = "java(modify(source.getB()))")
    @Mapping(target = "a", expression = "java(String.valueOf(source.getA()))")
    Target sourceToTarget(Source source);

    default String modify(int value) {
        return "prefix_"   value;
    }
}

It has generated this code :

@Component
public class ExampleMapperImpl implements ExampleMapper {

    @Override
    public Target sourceToTarget(Source source) {
        if ( source == null ) {
            return null;
        }

        Target target = new Target();

        target.setB( modify(source.getB()) );
        target.setA( String.valueOf(source.getA()) );

        return target;
    }
}

--> Try with @Named :

@Mapper(componentModel = "spring")
public interface ExampleMapper {

    @Mapping(source = "a", target = "a")
    @Mapping(target = "b", expression = "java(modify(source.getB()))")
    Target sourceToTarget(Source source);

    @Named("any-name")
    default String modify(int value) {
        return "prefix_"   value;
    }
}

Note : I have used @Named on modify(int value) method so that it can't be used by any mapping unless mentioned via qualifiedByName or explicitly called via expression.

It has generated this code :

@Component
public class ExampleMapperImpl implements ExampleMapper {

    @Override
    public Target sourceToTarget(Source source) {
        if ( source == null ) {
            return null;
        }

        Target target = new Target();

        target.setA( String.valueOf( source.getA() ) );

        target.setB( modify(source.getB()) );

        return target;
    }
}
  • Related