Spring & mapstruct, generate a mapper using a parameterized constructor? - java

Mapstruct always generate mappers that use a default empty constructor and setters to construct an object.
I need mapstruct to construct objects using parameterized constructor instead.
I have this mapper configured using maptruct:
#Mapper(componentModel = "spring")
public interface FlightMapper {
FlightBooking flightBookingPostRequestDtoToFlightBooking(FlightBookingPostRequestDto flightBookingDto);
}
The implementation that mapstruct generates uses the default constructor without parameters:
public FlightBooking flightBookingPostRequestDtoToFlightBooking(FlightBookingPostRequestDto flightBookingDto) {
if ( flightBookingDto == null ) {
return null;
}
FlightBooking flightBooking = new FlightBooking();
flightBooking.setFlight_booking_id( flightBookingDto.getBooking_id() );
return flightBooking;
}
But I need this implementation with a parameterized constructor instead:
public FlightBooking flightBookingPostRequestDtoToFlightBooking(FlightBookingPostRequestDto flightBookingDto) {
if ( flightBookingDto == null ) {
return null;
}
FlightBooking flightBooking = new FlightBooking(flightBookingDto.getBooking_id());
return flightBooking;
}
How can I configure mapstruct to achieve this? I haven't found any responses in the official documentation. Thanks in advance

Since the 1.4 release, MapStruct supports using constructors when instantiating mapping targets.
One solution would be marking the parametrized constructor as #Default. For more possible solutions and more details on how the Constructor support works have a look at the reference guide.

ObjectFactory is the way to go.
By default beans are created during the mapping process with the
default constructor. If a factory method with a return type that is
assignable to the required object type is present, then the factory
method is used instead.
Factory methods can be defined without parameters, with an #TargetType
parameter, a #Context parameter, or with the mapping source parameter.
If any of those parameters are defined, then the mapping method that
is supposed to use the factory method needs to be declared with an
assignable result type, assignable context parameter, and/or
assignable source types.
I left to you the pleasure of reading documentation and solve the problem :)

Related

Mapstruct - Convert Object who contains and interface into himself

i'm using mapstruct for converting an object to another.
Into the object to convert, there's an interface, and mapstruct doesn't like that.
I was able to convert an interface to an object by implementing the default of the method and specifing the implementation to call:
public default MessagesList interfaceMapping (Integer not, List<MessageEntity> list) {
return messToImpl(numNotification, list);
}
Now the problem is that i don't know how to do a similar thing that is not a workaround, to convert an internal object signed as interface.
Just find out a good way to implement a custom code for a single object mapping:
#Mapping(target = "sender", expression = "java(new YourClass(null, messageEntity.getSenderType(), messageEntity.getSenderID(), messageEntity.getSenderContact()))")
In this way, through the expression you can define a custom code still using mapstruct definitions.
Just in case you could need to import a class not defined as source or target, just remember to annotate the class as following, to allow mapstruct to import the required class:
#Mapper(imports = YourClass.class)

Guice - No implementation was bound (for List<String>)

I am trying to bind an instance of List when a List is annotated with specific annotation.
I tried using Instance Binding and Provider methods, but I keep getting the error.
Here is my #Provides method and configure()
#Provides #Named("Regions")
public List<String> getRegions() {
return AppConfig.findVector("Regions"); //this would return a Vector<String>
}
#Override
protected void configure() {
bind(List.class).annotatedWith(Names.named("Regions")).to(Vector.class);
}
Here's how I try to fetch the instance -
List<String> regions = injector.getInstance(Key.get(List.class, Names.named("Regions")));
This is the error that I get
com.google.inject.ConfigurationException: Guice configuration errors:
1) No implementation for java.util.List annotated with #com.google.inject.name.Named(value=Regions) was bound.
while locating java.util.List annotated with #com.google.inject.name.Named(value=Regions)
To Guice, a List is different from a List<String>, and a #Named("Regions") List is different from a #Named("Regions") List<String>.
Use a new TypeLiteral<List<String>>() {} (N.B. generically-typed anonymous inner class, as that is necessary to get non-erased generics at runtime) in your Key.get to request a generic type from Guice.
Separately, your bind statement isn't helping anything, and can be removed. You'll need to manually convert your Vector into a List in your #Provides method, but bind won't help you do that.

MapStruct: Object.class to Custom.class mapping

I'm a newbie with MapStruct, and need some help with that.
I have a Source class, with an attribute
Object input;
Which, in runtime, returns a custom object named TicketDetails.
Now, in my target class there is a attribute named,
MyTicket myTicket;
which, I need to map with an attribute of TicketDetails object.
For, better understanding, I'm writing the normal java code example below.
SourceClassModel sourceClassModel = new SourceClassModel();
TargetClassModel targetClassModel = new TargetClassModel();
//mapping
TicketDetails ticketDetails = (TicketDetails) sourceClassModel.getInput();
targetClassModel.setMyTicket(ticketDetails.getMyTicket);
Now, my question is, how to achieve this case using MapStruct?
Either on a used mapper (see #Mapper#uses()) or in a non-abstract method on the mapper itself - in case it is an abstract class and not an interface - define the mapping from Object to TicketDetails yourself:
TicketDetails asTicketDetails(Object details) {
return (TicketDetails) details;
}
The generated method for the conversion of SourceClassModel to TargetClassModel will then invoke that manually written method for converting the myTicket property.

Spring/json: Convert a typed collection like List<MyPojo>

I'm trying to marshal a list: List<Pojo> objects via the Spring Rest Template.
I can pass along simple Pojo objects, but I can't find any documentation that describes how to send a List<Pojo> objects.
Spring is using Jackson JSON to implement the HttpMessageConverter. The jackson documentation covers this:
In addition to binding to POJOs and
"simple" types, there is one
additional variant: that of binding to
generic (typed) containers. This case
requires special handling due to
so-called Type Erasure (used by Java
to implement generics in somewhat
backwards compatible way), which
prevents you from using something like
Collection<String>.class (which does
not compile).
So if you want to bind data into a
Map<String,User> you will need to use:
Map<String,User> result = mapper.readValue(src, new TypeReference<Map<String,User>>() {});
where TypeReference is only needed to
pass generic type definition (via
anynomous inner class in this case):
the important part is
<Map<String,User>> which defines type
to bind to.
Can this be accomplished in the Spring template? I took a glance at the code and it makes me thing not, but maybe I just don't know some trick.
Solution
The ultimate solution, thanks to the helpful answers below, was to not send a List, but rather send a single object which simply extends a List, such as: class PojoList extends ArrayList<Pojo>. Spring can successfully marshal this Object, and it accomplishes the same thing as sending a List<Pojo>, though it be a little less clean of a solution. I also posted a JIRA in spring for them to address this shortcoming in their HttpMessageConverter interface.
In Spring 3.2 there is now support for generic types using the new exchange()-methods on the RestTemplate:
ParameterizedTypeReference<List<MyBean>> typeRef = new ParameterizedTypeReference<List<MyBean>>() {};
ResponseEntity<List<MyBean>> response = template.exchange("http://example.com", HttpMethod.GET, null, typeRef);
Works like a charm!
One way to ensure that generic type parameters are included is to actually sub-class List or Map type, such that you have something like:
static class MyStringList extends ArrayList<String> { }
and return instance of that list.
So why does this make a difference? Because generic type information is retained in just a couple of places: method and field declarations, and super type declarations. So whereas "raw" List does NOT include any runtime type information, class definition of "MyStringList" does, through its supertype declarations.
Note that assignments to seemingly typed variables do not help: it just creates more compile-time syntactic sugar: real type information is only passed with Class instances (or lib-provided extensions thereof, like JavaType and TypeReference in Jackson's case).
Other than this, you would need to figure out how to pass Jackson either JavaType or TypeReference to accompany value.
If I read the docs for MappingJacksonHttpMessageConverter right, you will have to create and register a subclass of MappingJacksonHttpMessageConverter and override the getJavaType(Class<?>) method:
Returns the Jackson JavaType for the
specific class. Default implementation
returns
TypeFactory.type(java.lang.reflect.Type),
but this can be overridden in
subclasses, to allow for custom
generic collection handling. For
instance:
protected JavaType getJavaType(Class<?> clazz) {
if (List.class.isAssignableFrom(clazz)) {
return TypeFactory.collectionType(ArrayList.class, MyBean.class);
} else {
return super.getJavaType(clazz);
}
}
I have solved this problem by using the following configuration:
private static final String POJO_ARRAY_LIST = PojoArrayList.class.getCanonicalName();
#Bean
public HttpMessageConverter<Object> httpMessageConverter() {
HttpMessageConverter<Object> httpMessageConverter = new MappingJackson2HttpMessageConverter() {
#Override
protected JavaType getJavaType(Type type, #Nullable Class<?> contextClass) {
JavaType javaType;
if (type != null && POJO_ARRAY_LIST.equals(type.getTypeName())) {
ObjectMapper objectMapper = new ObjectMapper();
TypeFactory typeFactory = objectMapper.getTypeFactory();
CollectionType collectionType = typeFactory.constructCollectionType(ArrayList.class, Pojo.class);
javaType = collectionType;
} else {
javaType = super.getJavaType(type, contextClass);
}
return javaType;
}
};
return httpMessageConverter;
}
where PojoArrayList is a final class that extends ArrayList<Pojo>.

Java annotation dynamic typecast

I have 2 java annotation types, let's say XA and YA. Both have some method(). I parse the source code and retrieve Annotation object. Now I'd like to dynamicaly cast the annotation to the real type of it to be able to call the method(). How can I do it without the instanceof statement? I really want to avoid switch-like source. I need something like this:
Annotation annotation = getAnnotation(); // I recieve the Annotation object here
String annotationType = annotation.annotationType().getName();
?_? myAnnotation = (Class.forName(annotationType)) annotation;
annotation.method(); // this is what I need, get the method() called
?_? means I have no idea what would be myAnnotation type. I cannot use the base class for my XA and YA annotations since the inheritance in annotations is not allowed. Or is it possible to do somehow?
Thanks for any suggestion or help.
Why don't you use the typesafe way to retrieve your annotation ?
final YourAnnotationType annotation = classType.getAnnotation(YourAnnotationType.class);
annotation.yourMethod();
If your annotation can't be found, null is returned.
Please note that this also works with fields and methods.
One way is to invoke the method dynamically using the name of it:
Annotation annotation = getAnnotation();
Class<? extends Annotation> annotationType = annotation.annotationType();
Object result = annotationType.getMethod("method").invoke(annotation);
This approach is quite risky and totally compromise the code refactoring if needed.

Categories

Resources