Search code examples
javamappinghierarchymapstruct

Mapstruct : How do I map hierarchical structure where the child object is a list and have attributes which are lists


I have a scenario where I need to map from a DTO object to a Domain objects.

I have simplified the scenario below

class TopLevel {
    private String attr1;
    private String attr2;
    private List<SecondLevel> secondLevel = new ArrayList<>();
    
    class SecondLevel {
        private String attr3;
        private JsonNode additionalConfig;
        private List<String> stringList = new ArrayList<>();
        private List<ThirdLevel> thirdLevel = new ArrayList<>();

        class ThirdLevel {
            private String attr4;
            private String attr5;
            private List<String> anotherList = new ArrayList<>();
            
        }  

    }  

}

This needs to be mapped to the domain structure below

class TopLevelDomain {
    private ID guid1; //Generated Guid
    private String attr1;
    private String attr2;
    private List<SecondLevelDomain> secondLevelDomain = new ArrayList<>();
}

class SecondLevelDomain {
    private ID guid1; //From TopLevel Domain
    private ID guid2; //Generated Guid
    private String attr3;
    private String stringListAsString; // List of Strings needs to be converted to string (JSON format)
    private String additionalConfig; //JSON node needs to be converted to String (JSON format)
    private List<ThirdLevelDomain> thirdLevelDomain = new ArrayList<>();
}

class ThirdLevelDomain {
    private ID guid1; //From TopLevel Domain
    private ID guid2; //From SecondLevel Guid
    private ID guid3; //New Guid
    private String attr4;
    private String attr5;
    private String anotherListAsString; // List of Strings needs to be converted to string (JSON format)

}

Now I need to do a qualifiedByName/Named mapping to get the GUID for the first level. Also I need similar GUID for the other 2 child levels as well.

@Mapping(source = "topLevel", target = "guid1", qualifiedByName = "mapGuid1")
@Mapping(source = "topLevel", target = "secondLevelDomain", qualifiedByName = "secondLevelDomain")
TopLevelDomain toTopLevel(TopLevel topLevel)

If I do something like the above code, I see no way how I can access the secondlevel attributes attr3, additionalConfig and the List stringList in the @Mapping annotation

Also how do I access the thirdlevel attributes attr4, attr5 and anotherList. I need to to direct mapping for the string mattributes, assign the guids and do some custom mapping for the List of String and JSONNodes


Solution

  • There are 2 things that you want to do here.

    1. Add additional mapping methods. Mapstruct will use user-defined mapping methods over generating new ones.
    2. Use @AfterMapping to pass on the generated GUID's.
    // custom mapping annotations
    TopLevelDomain toTopLevel( TopLevel topLevel );
    
    // custom mapping annotations
    SecondLevelDomain toSecondLevel( TopLevel.SecondLevel secondLevel );
    
    // custom mapping annotations
    ThirdLevelDomain toThirdLevel( TopLevel.SecondLevel.ThirdLevel thirdLevel );
    
    // custom mapping methods
    
    @AfterMapping
    default void handleGuidTransfer( TopLevelDomain topLevel ){
        for ( SecondLevelDomain secondLevel: topLevel.getSecondLevelDomain() ) {
            secondLevel.setGuid1( topLevel.getGuid1() );
            for ( ThirdLevelDomain thirdLevel: secondLevel.getThirdLevelDomain() ) {
                thirdLevel.setGuid1( topLevel.getGuid1() );
                thirdLevel.setGuid2( secondLevel.getGuid2() );
            }
        }
    }