publicissapient-france/selma

Level control for nested objects

surdsey94 opened this issue · 4 comments

Hie,
I read the whole documentation but I didn't found a simple way to limit the mapping deep between root mapping and nested objects.
Is there a solution to name the root mapping method to ignore some nested objects ?
Or to annotate to ignore some nested objects. Purpose remains not to generate custom DTO whereas original ones could in some applications map links too deep.

Thank you for your help.

Hi,

you can not decide the generated mapping deep level. But you can still ignore specific fields using the withIgnoreFields.
So the solution is to ignore the nested fields. Since this expects properties names, you should define a mapping method on the bean to bean level you want to ignore.

If you can share a simple source code of the bean graph and mapping method pointing the fields to be ignored. I'll be happy to explain by code the solution.

Hi back.
Thank you for quick answering. My full purpose is to get custom mapping to control by name what mapper will do in such kind of configuration:

UserDto {
private Collection roles;
}

UserRoleDto {
private CompanyDto company;
private RoleDto role;
}

public interface Mapper {
// To get fully mapped userDto
UserDto asUserDto(User user);

// To get empty linking object.
@maps(withIgnoreFields = {"roles"})
UserDto asUserDtoLight(User user)

// To get deep-controlled mapping.
@maps(withIgnoreFields = {
"roles.company"
})
UserDto asUserDtoWithRoleOnly(User user)
}

Here the asUserDtoWithRoleOnly should map nested object and avoid deeper mapping. The result would be very interesting for serialization and performances not to load the whole map especially in complex linking models.

I can imagine two possible enhancements:

  • to control "sub-nested" mapping: example in user, "roles.company"
  • specify in the @maps which autogenerated method to call: example:

@maps(nested = {@nested({"roles", "asUserRoleWithoutCompany"})})
UserDto asUserDtoWithRoleDeepOne(User user);

@maps(withIgnoreFields = {"company"})
UserRoleDto asUserRoleWithoutCompany(UserRole userRole)

I'll be glad to integrate your solution because for the moment in Spring context my controller is not able to return a light serialized version of the user since it comes without role or it comes with role and through it company, information I don't want to share.

Thank you in advance.

Hi again,

for now you can define two different mappers like that :

@Mapper
public interface Mapper {
    // To get fully mapped userDto
    UserDto asUserDto(User user);

    // To get empty linking object.
    @maps(withIgnoreFields = {"roles"})
    UserDto asUserDtoLight(User user)
}

@Mapper
public interface RolesOnlyMapper {

    // To get userDto
    UserDto asUserDtoWithRoleOnly(User user);

    @Maps(withIgnoreFields = "company")
    RoleDto asRoleDto(Role role);
}

Another way would be to use a generated mapper as custom mapper of the first one which is more or less like your second solution

@Mapper
public interface Mapper {
    // To get fully mapped userDto
    UserDto asUserDto(User user);

    // To get empty linking object.
    @Maps(withIgnoreFields = {"roles"})
    UserDto asUserDtoLight(User user);
    
    @Maps(withCustomFields = {
            @Field(value = "roles", withCustom = RolesOnlyMapper.class)
    })
    UserDto asUserDtoWithRoleOnly(User user);

}

@Mapper
public interface RolesOnlyMapper {

    @Maps(withIgnoreFields = "company")
    RoleDto asRoleDto(Role role);

}

Implementing a nested ignore field is complex because we do not keep track of the visited path between generated mapping methods. But the second, solution you mention is interesting. I'll think about a proper way to support thiskind of configuration.

Hope, this helps.

Thank you for your new help.

I have to says that I already thought about that solution. Using separated custom mappers seems to be the best solution for the moment but is heavy since our system uses a lot of entities and quite differents deep-controlled requirements.
We will use it but please keep me in touch if you decides to integrate a simpler solution.

Cordially.