-1

I'm trying to map next structure(succesfully map in dozer, have some problems with mapstruct) list is absent in source, how can I get its first element in mapper? Target class:

    @Data
public class FirstLvl {
        protected List<SecondLvl> list;
        
        @Data
        public static class SecondLvl {
                protected FirstLvl.SecondLvl.ThirdLvl thirdLvl;

                @Data
                public static class ThirdLvl {
                        protected FourthLvl fourthLvl;
                }
        }
}

@Data
public class FourthLvl {
        protected FourthLvl.FithLvl fithLvl;

        @Data
        public static class FithLvl {

                protected String str1;
                protected String str2;

        }
}

Source:

@Data
public class FirstLvlDTO {
      protected FirstLvlDTO.SecondLvlDTO secondLvlDTO ;

        @Data
        public static class SecondLvlDTO  {
            protected FirstLvlDTO.SecondLvlDTO.ThirdLvlDTO thirdLvlDTO ;


                @Data
                public static class ThirdLvlDTO {

                        protected FourthLvlDTO fourthLvlDTO;
                }
        }

}

@Data
public class FourthLvlDTO  {

        protected String str1;

        protected String str2;

}

Mapper and Object factory:

@Mapper(uses = { ObjectFactory.class })
public interface FirstLvlDTOtoFirstLvl  {

@Mapping(target="list[0].thirdLvl.fourthLvl.fithLvl.str2", source="secondLvlDTO.thirdLvlDTO.fourthLvlDTO.str2")

    FirstLvl toFirstLvl (FirstLvlDTO firstLvlDTO );
        
}

public class ObjectFactory {

    public FirstLvl createFirstLvl() {
        return new FirstLvl();
    }
    
    public FirstLvl.SecondLvl createFSLvl() {
        return new FirstLvl.SecondLvl();
    }
}

The problem is I don't know how to get first element of List([] notation is not used in mapstruct). I can add methods getlist0/setlist0 to class FirstLvl for simply returning first element of List(return list.get(0)) and use it in mapper like list0.thirdLvl.fourthLvl.fithLvl.str2, but I don't know is it good idea and is it possible to do it better List is absent in FirstLvlDTO (source), so I really can't understand how to map it and set this nested structure to first element of List

ovod
  • 49
  • 1
  • 7

2 Answers2

1

I am not sure that i completly understand your Code. My approach would look something like this. However your types in the list seem not to match.

public interface TestMapper {

    @Mapping(target = "list0", ignore = true)
    @Mapping(target = "list", ignore = true)
    A toA(A1 a1);

    @AfterMapping
    default void mapperCode(@MappingTarget A a, A1 a1) {
        List<A.B> list = a.getList(); // target list requiures Type A.B 
        A1.B1 b1 = a1.getB1(); // source does have A1.B1 and not A.B as type
        // if your Types match set the list here manually like 
        // a.setList(List.of(a1.getB()));
    }

}

Your code is har dto read using only A,B,A1,B1,... . I Would suggest you use real names.

EDIT:

a Working but really ugly solution would be doing it manually. I am not sure if that helps you.

public interface TestMapper {

@Mapping(target = "list", ignore = true)
FirstLvl map(FirstLvlDTO firstLvlDTO);

@AfterMapping
default void mapperCode(@MappingTarget FirstLvl firstLvl, FirstLvlDTO firstLvlDTO) {
    String str1 = firstLvlDTO.getSecondLvlDTO().getThirdLvlDTO().getFourthLvlDTO().getStr1();
    String str2 = firstLvlDTO.getSecondLvlDTO().getThirdLvlDTO().getFourthLvlDTO().getStr2();
    FirstLvl.SecondLvl secondLvl = new FirstLvl.SecondLvl();
    FirstLvl.SecondLvl.ThirdLvl thirdLvl = new FirstLvl.SecondLvl.ThirdLvl();
    secondLvl.setThirdLvl(thirdLvl);
    FourthLvl fourthLvl = new FourthLvl();
    thirdLvl.setFourthLvl(fourthLvl);
    FourthLvl.FithLvl fithLvl = new FourthLvl.FithLvl(str1, str2);
    fourthLvl.setFithLvl(fithLvl);
    firstLvl.setList(Arrays.asList(secondLvl));
}

}

@Data
public class FourthLvl {
protected FourthLvl.FithLvl fithLvl;

@Data
public static class FithLvl {

    public FithLvl(String str1, String str2){
        this.str1 = str1;
        this.str2 = str2;
    }

    protected String str1;
    protected String str2;

}

}

GJohannes
  • 1,408
  • 1
  • 8
  • 14
  • thanks, I updated names of classes, I need to map str2 to str2 in list[0], but source hasn't list and its not clear how map that – ovod Apr 12 '22 at 12:33
1

Mapstruct does not support many-to-one or one-to-many mappings, these need to be manually implemented.

here you find a working example for your classes:

@Mapper
public abstract class LevelMapper {

    @Mapping( target = "secondLvlDTO", source = "list" )
    public abstract FirstLvlDTO map(FirstLvl lvl);

    // used by the manual implementation to continue mapping.
    @Mapping( target = "thirdLvlDTO.fourthLvlDTO", source = "thirdLvl.fourthLvl.fithLvl" )
    protected abstract SecondLvlDTO map(SecondLvl lvl);

    // manually implementation for getting the first object from the list and converting it.
    protected SecondLvlDTO getFirst(List<SecondLvl> list) {
        return map( list.get( 0 ) );
    }

// the reverse
    
    @Mapping( target = "list", source = "secondLvlDTO" )
    public abstract FirstLvl map(FirstLvlDTO lvl);

    // used by the manual implementation to continue mapping.
    @Mapping( target = "thirdLvl.fourthLvl.fithLvl", source = "thirdLvlDTO.fourthLvlDTO" )
    protected abstract SecondLvl map(SecondLvlDTO lvl);

    // manually implementation for creating the the list and filling it with the converted dto.
    protected List<SecondLvl> asList(SecondLvlDTO dto) {
        return Arrays.asList( map( dto ) ); // or any other way to create a list.
    }
}
Ben Zegveld
  • 1,186
  • 2
  • 6
  • hi, thanks for your answer. but I need to map vice versa FirstLvlDTO to FirstLvl and FirstLvlDTO doesn't contain list(I change FirstLvlDTO to FirstLvl places, but in the last method : // here is the problem - there is no List in FirstLvlDTO `protected SecondLvl getFirst(List list) { return map( list.get( 0 ) ); }` – ovod Apr 13 '22 at 08:37
  • it's basically the same, but in reverse. Added it to the example in the post. – Ben Zegveld Apr 13 '22 at 16:09
  • thanks, but could you please explain a bit more about the mapping logic? – ovod Apr 14 '22 at 14:11
  • currently I'm trying to map another field MegaLvl in FirstLvlDTO to MegaLvl in SecondLvl . did it only using @MappingTarget and set directly.tried to do something like this, but unsuccessful: @Mapping(target = "MegaLvl", source = "lvl.MegaLvl") FirstLvl.SecondLvl mapMegaLvl(FirstLvlDTO lvl); default FirstLvl.SecondLvl MegaLvl(FirstLvlDTO lvl) { return mapMSGLVL(lvl); }; – ovod Apr 14 '22 at 14:48