Here is my context: I am using byteBuddy to dynamically generate a class that transform an object into another one based on a external configuration. I encountered some issues and I wanted to find an alternative that's how I discovered MapStruct.
So I tried to build simple mapper and I wanted to know if there is the possibility to customize the annotation to add transformation functions. For instance I would like to have:
@Mapping(
source = "mySourceField",
sourceType = "String",
target = "myTargetField",
targetType = "Integer",
transformation = {"toInteger", "toSquare"}
),
And on the mapper implementation I would have something like :
public TypeDest toSiteCatTag(TypeSrc obj) {
if ( obj == null ) {
return null;
}
TypeDest objDest = new TypeDest();
objDest.myTargetField = Formatter.toSquare(
Formatter.toInteger(obj.mySourceField));
return objDest;
}
If someone can help me to achieve that I would be grateful and it would save me a lot of time.
Thanks in advance.
A MapStruct mapper is an interface or an abstract class annotated with @Mapper . This special annotation is used by the MapStruct code generator to automatically generate a working implementation of this Java file at build-time.
@Mapping annotation is used when the field names in the source and destination class differ. For example, The below code would map the fullName field in the source class object to the name field in the destination class object. This is particularly useful when you are mapping objects of two different classes.
Using Mapstruct we can map list in similar fashion as we map primitives. To get a list of objects, we should provide a mapper method which can map an object.
If your 2 types TypeDest
and TypeSrc
are not generated on runtime, i.e. they are your compiled classes, then you can achieve what you want. MapStruct does not work on runtime as it is an Annotation Processor and generates java code. If there are some issues, like you are trying to map non-existing fields or there are ambiguous mapping methods then you will get compile time errors.
It will look something like:
@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));
}
}
There is a way to do it without the custom method in the mapper, but you'll need to have a method somewhere that applies the toInteger
and then toSquare
transformation. If you have a method with the signature Integer squaredString(String obj)
in your Formatter
.
e.g.
@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
}
Then you can do this in your mapper:
@Mapper(uses = { Formatter.class })
public interface MyMapper {
@Mapping(source = "mySourceField", target = "myTargetField", qualifiedBy = SquaredString.class)
TypeDest toSiteCatTag(TypeSrc obj);
}
The examples above will only be applied to the specific mapping since qualifedByName
/ qualified
is used. If you want to have a different way of converting a String
to Integer
, then you can define a method either in your Mapper or in some of the classes in Mapper#uses
with the signature Integer convertString(String obj)
. MapStruct will then delegate the conversion from String
to Integer
to this method.
You can find more about mapping with qualifiers here in the reference documentation, and here for more information regarding the mapping method resolution.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With