Tim
Tim

Reputation: 4274

How to implement a partial custom mapping with MapStruct?

I would like MapStruct to map every property of my Object, except for one particular one for which I would like to provide a custom mapping.

So far, I implemented the whole mapper myself, but every time I add a new property to my Entity, I forget to update the mapper.

@Mapper(componentModel = "cdi")
public interface MyMapper {
    MyMapper INSTANCE = Mappers.getMapper(MyMapper.class);

    default MyDto toDTO(MyEntity myEntity){
        MyDto dto = new MyDto();
        dto.field1 = myEntity.field1;
        // [...]
        dto.fieldN = myEntity.fieldN;

        // Custom mapping here resulting in a Map<> map
        dto.fieldRequiringCustomMapping = map;

    }
}

Is there a way to outsource the mapping for my field fieldRequiringCustomMapping and tell MapStruct to map all the other ones like usual? 🤔

Upvotes: 3

Views: 5264

Answers (1)

Filip
Filip

Reputation: 21393

MapStruct has a way to use custom mapping between some fields. So in your case you can do someting like:

@Mapper(componentModel = "cdi")
public interface MyMapper {

    @Mapping(target = "fieldRequiringCustomMapping", qualifiedByName = "customFieldMapping")
    MyDto toDTO(MyEntity myEntity);

    // The @(org.mapstruct.)Named is only needed if the Return and Target type are not unique
    @Named("customFieldMapping")
    default FieldRequiringCustomMapping customMapping(SourceForFieldRequiringCustomMapping source) {
        // Custom mapping here resulting in a Map<> map
        return map
    }
    
}

I do not know from what your fieldRequiringCustomMapping needs to be mapped, the example assumes that you have such a field in MyEntity as well, if that is not the case you'll need to add source to the @Mapping.


Side Note: When using a non default componentModel in your case cdi it is not recommended to use the Mappers factory. It will not perform the injection of other mappers in case you use them in a mapper.

Upvotes: 7

Related Questions