Search code examples
mappingmapstruct

Using Mapstruct to map elements from List records to an object attributes


We are using Mapstruct in our project for mapping entities to the response we send through the rest api. For most parts, it is working. but we have one very specific scenario where I have tried to research a lot, but could not get anything for it. Here's what I have in my Mapper Interface:

TargetObject mapSourcesToTarget(SourceObject1 source1, List<SourceObject2> source2List);

source2List can have maximum of 3 records and in case, record is not there, we can just map to null. but here's how we want the mapping to be:

source2List[0].field1 -> TargetObject.status1
source2List[0].field2 -> TargetObject.code1
source2List[1].field1 -> TargetObject.status2
source2List[1].field2 -> TargetObject.code2
source2List[2].field1 -> TargetObject.status3
source2List[2].field2 -> TargetObject.code3

Is this something that is doable with Mapstruct?

We have tried

@Mapping(target = "status1", source = "source2List[0].field1")

but as expected, this didn't work.


Solution

  • If you cannot transform list into some entity before passing to mapper (which could simplify mapper logic and make whole solution more straightforward), I can suggest two options.

    1. Use expression:
        @Mapper
        public interface MyMapper1 {
            @Mapping(target = "status1", expression = "java(0 >= source2List.size() ? null : source2List.get(0).field1)")
            @Mapping(target = "code1", expression = "java(0 >= source2List.size() ? null : source2List.get(0).field2)")
            @Mapping(target = "status2", expression = "java(1 >= source2List.size() ? null : source2List.get(1).field1)")
            @Mapping(target = "code2", expression = "java(1 >= source2List.size() ? null : source2List.get(1).field2)")
            @Mapping(target = "status3", expression = "java(2 >= source2List.size() ? null : source2List.get(2).field1)")
            @Mapping(target = "code3", expression = "java(2 >= source2List.size() ? null : source2List.get(2).field2)")
            TargetObject mapSourcesToTargetWithExpression(SourceObject1 source1, List<SourceObject2> source2List);
        }
    
    1. Use special method with manual preprocessing of list items:
        @Mapper
        public interface MyMapper2 {
    
            default TargetObject mapSourcesToTarget(SourceObject1 source1, List<SourceObject2> source2List) {
                return mapSourcesToTarget(source1, get(source2List, 0), get(source2List, 1), get(source2List, 2));
            }
    
            @Mapping(target = "status1", source = "firstSourceObject2.field1")
            @Mapping(target = "code1", source = "firstSourceObject2.field2")
            @Mapping(target = "status2", source = "secondSourceObject2.field1")
            @Mapping(target = "code2", source = "secondSourceObject2.field2")
            @Mapping(target = "status3", source = "thirdSourceObject2.field1")
            @Mapping(target = "code3", source = "thirdSourceObject2.field2")
            TargetObject mapSourcesToTarget(SourceObject1 source1,
                                            SourceObject2 firstSourceObject2,
                                            SourceObject2 secondSourceObject2,
                                            SourceObject2 thirdSourceObject2);
    
            private SourceObject2 get(List<SourceObject2> source2List, int index) {
                return index >= source2List.size() ? null : source2List.get(index);
            }
        }