modelmapper / modelmapper

Intelligent object mapping
http://modelmapper.org
Apache License 2.0
2.26k stars 343 forks source link

Generic mapping using reflection - ModelMapper #747

Open ImKumin opened 2 weeks ago

ImKumin commented 2 weeks ago

How can I automatically map models like the ones below?

public class RewardClaim {
    private PlayerEntity owner; // PlayerEntity extends Entity which has a .getId()
}

Maps to

public class RewardClaimResponseModel {
    private String ownerId;
}

It WORKS fine if I manually implement this:

Converter<Entity, String> entityToEntityIdConverter = context -> {
    Entity entity = context.getSource();
    return entity != null ? entity.getId() : null;
};

modelMapper.typeMap(RewardClaim.class, RewardClaimResponseModel.class)
        .addMappings(mapper -> mapper.using(entityToEntityIdConverter)
                .map(RewardClaim::getOwner, RewardClaimResponseModel::setOwnerId));

However, this type of mapping from Entity to String Id, is supposed to be done a lot of times, and I don't want to manually add each mapping like the one above. So I need to generalize a solution. Another example is like this:

private UserEntity user; -> private String userId; // Another example

So basically, something like this pseudo code

modelMapper.typeMap(Object.class, Object.class)
        .addMappings(mapper -> mapper.using(entityToEntityIdConverter)
                .map(FIND HERE THE get FUNCTION, FIND HERE THE set FUNCTION));

I do not understand how modelMapper fully works, but I've tried multiple ways of achieving this with converters and type maps using Reflection, but without success.

One of the last tries is here, but without any idea of how to get the sourceClass and destClass.

Converter<Entity, String> entityToEntityIdConverter = context -> {
    Entity entity = context.getSource();
    return entity != null ? entity.getId() : null;
};

modelMapper.typeMap(Object.class, Object.class).addMappings(mapper -> {
    Class sourceClass = null; //TODO: HOW TO GET THIS DYNAMICALLY?
    Class destinationClass = null; //TODO: HOW TO GET THIS DYNAMICALLY?

    for (Method setMethod : destinationClass.getDeclaredMethods()) {
        if (setMethod.getName().endsWith("Id") && setMethod.getReturnType() == String.class) {
            String propertyName = setMethod.getName().substring(3, setMethod.getName().length() - 2); // Remove "set" and "id"
            try {
                Method getMethod = sourceClass.getMethod("get" + propertyName, sourceClass);
                mapper.using(entityToEntityIdConverter).map(src -> {
                    // Try catch here removed for simplicity
                    return getMethod.invoke(src);
                }, (dest, v) -> {
                    setMethod.invoke(dest, v);
                });
            } catch (NoSuchMethodException e) {
                // Setter method not found, ignore
            }
        }
    }
});

This seems so doable, but I've been having no luck. Any idea of how I can achieve this?

WatchdataGitHub commented 2 weeks ago

这是来自QQ邮箱的假期自动回复邮件。   您好,我最近正在休假中,无法亲自回复您的邮件。我将在假期结束后,尽快给您回复。