web-dev-qa-db-fra.com

MapStruct: enrichir l'annotation de mappage pour définir un mappeur personnalisé

Voici mon contexte: j'utilise byteBuddy pour générer dynamiquement une classe qui transforme un objet en un autre basé sur une configuration externe. J'ai rencontré des problèmes et je voulais trouver une alternative, c'est ainsi que j'ai découvert MapStruct.

J'ai donc essayé de construire un mappeur simple et je voulais savoir s'il y avait la possibilité de personnaliser l'annotation pour ajouter des fonctions de transformation. Par exemple, j'aimerais avoir:

@Mapping(
    source = "mySourceField", 
    sourceType = "String",
    target = "myTargetField",
    targetType = "Integer",
    transformation = {"toInteger", "toSquare"}
),

Et sur la mise en œuvre du mappeur, j'aurais quelque chose comme:

 public TypeDest toSiteCatTag(TypeSrc obj) {

    if ( obj == null ) {

        return null;
    }

    TypeDest objDest = new TypeDest();

    objDest.myTargetField = Formatter.toSquare(
        Formatter.toInteger(obj.mySourceField));

    return objDest;
}

Si quelqu'un peut m'aider à y parvenir, je lui en serais reconnaissant et cela me ferait gagner beaucoup de temps.

Merci d'avance.

10
nbchn

Si vos 2 types TypeDest et TypeSrc ne sont pas générés à l'exécution, c'est-à-dire qu'ils sont vos classes compilées, alors vous pouvez obtenir ce que vous voulez. MapStruct ne fonctionne pas à l'exécution car il s'agit d'un processeur d'annotation et génère Java. S'il y a des problèmes, comme vous essayez de mapper des champs inexistants ou il existe des méthodes de mappage ambiguë, vous obtiendra des erreurs de temps de compilation.

Cela ressemblera à quelque chose comme:

@Mapper
public interface MyMapper {

    @Mapping(source = "mySourceField", target = "myTargetField", qualifiedByName = "myTransformation")// or you can use a custom @Qualifier annotation with qualifiedBy
    TypeDest toSiteCatTag(TypeSrc obj);

    @Named("myTransformation")// or your custom @Qualifier annotation
    default Integer myCustomTransformation(String obj) {
        return Formatter.toSquare(Formatter.toInteger(obj));
    }
}

Il existe un moyen de le faire sans la méthode personnalisée dans le mappeur, mais vous devrez avoir une méthode quelque part qui applique la transformation toInteger puis toSquare. Si vous avez une méthode avec la signature Integer squaredString(String obj) dans votre Formatter.

par exemple.

@Qualifier
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.CLASS)
public @interface SquaredString {}

public class Formatter {

    @SquaredString// you can also use @Named, this is just as an example
    public static Integer squaredString(String obj) {
        return toSquare(toInteger(obj));
    }
    //your other methods are here as well
}

Ensuite, vous pouvez le faire dans votre mappeur:

@Mapper(uses = { Formatter.class })
public interface MyMapper {

    @Mapping(source = "mySourceField", target = "myTargetField", qualifiedBy = SquaredString.class)
    TypeDest toSiteCatTag(TypeSrc obj);
}

Les exemples ci-dessus ne seront appliqués qu'au mappage spécifique puisque qualifedByName/qualified est utilisé. Si vous voulez avoir une façon différente de convertir un String en Integer, vous pouvez définir une méthode soit dans votre mappeur soit dans certaines des classes de Mapper#uses Avec le signature Integer convertString(String obj). MapStruct déléguera ensuite la conversion de String à Integer à cette méthode.

Vous pouvez trouver plus d'informations sur le mappage avec les qualificatifs ici dans la documentation de référence, et ici pour plus d'informations sur la résolution de la méthode de mappage.

19
Filip