How mapping to List<> with mapstruct - java

Hi i need mappig variables to request. How can i mapp into List.
My request looks like.
public class Request {
private String Id;
private List<Data> applicationData;
#Data
#NoArgsConstructor
#AllArgsConstructor
public static class Data {
private String data1;
private String data2;
private String data3;
}
}
and my mapper
#Mapper(componentModel = "spring")
public abstract class RequestMapper {
#Mapping(target = "Id", source = "data.Id")
#Mapping(target = "data.???.data1", source = "data.data1")
#Mapping(target = "data.???.data2", source = "data.data2")
#Mapping(target = "applicationData.???.data3", source = "data.data3")
public abstract Request map(Data variables);
}

From what I understand you want to map your values into a singleton list. You can achieve that by providing 2 new methods in your mapper.
You mapper would look like:
#Mapper(componentModel = "spring")
public abstract class RequestMapper {
#Mapping(target = "Id", source = "Id")
#Mapping(target = "data", source = "variables")
public abstract Request map(Data variables);
protected List<Data> mapToList(Data variables) {
return variables == null ? null : mapToApplication(variables);
}
//Add mappings if they are needed
protected abstract ApplicationData mapToApplication(Data variables);
}

Related

can't map property. Consider to declare/implement a mapping method. Can't generate mapping method from non-iterable type to iterable type

I can't seem to use mapstruct correctly
#Mapping(target = "products", source = "itemBookType")
SearchBookingResult backToTp(ItemBook itemBook);
When running this code I get the following error:
Can't map property "ProductType itemBookType" to "List<ProductOverview> products". Consider to declare/implement a mapping method: "List<ProductOverview> map(ProductType value)".
I added the following code at the bottom:
List<ProductOverview> map(ProductType value);
but still it returns me the following error:
Can't generate mapping method from non-iterable type to iterable type from java stdlib.
Itembook class:
public class ItemBook {
private ProductType itemBookType; //ProductType class
private Integer idref;
private String reference;
}
ProductType class:
public enum ProductType {
BOOK, PHONE, GAME
}
SearchBookingResult class:
public class SearchBookingResult extends BaseResponse<SearchBookingResult> {
private String reference;
private List<ProductOverview> products;
}
the Mapper
#Mapper(componentModel = "spring")
public interface ItemBookMapper {
ItemBookTpMapper INSTANCE = Mappers.getMapper(ItemBookTpMapper.class);
#Mapping(target = "reference", source = "idref")
#Mapping(target = "products", source = "itemBookType")
SearchBookingResult backToTp(ItemBook itemBook);
List<ProductOverview> map(ProductType value);
}
ProductOverView class is abstract:
public abstract class ProductOverview implements Serializable {
private ProductType productType;
public ProductOverview(ProductType productType) {
this.productType = productType;
}
public ProductType getProductType() {
return productType;
}
}
map reference work but products return many error.
MapStruct can't generate mapping method from non-iterable type to iterable type because it's impossible to create a generic mapping.
The only solution, as suggested by the exception, is to create a custom method where you can implement your own mapping algorithm.
#Mapper(componentModel = "spring")
public interface ItemBookMapper {
ItemBookMapper INSTANCE = Mappers.getMapper(ItemBookMapper.class);
#Mapping(target = "reference", source = "idref")
#Mapping(target = "products", source = "itemBookType", qualifiedByName = "mapProducts")
SearchBookingResult backToTp(ItemBook itemBook);
#Named("mapProducts")
default List<ProductOverview> mapProducts(ProductType value){
List<ProductOverview> products = new ArrayList<>();
//add your custom mapping implementation
return products;
}
}
Official documentation: adding-custom-methods
Baeldung tutorial: mapstruct-custom-mapper

how can we ignore the collections in MapStructs target response

how can we ignore the collections in target response.
public class ClassSource{
private int id;
}
Destination :
public class ClassDestination {
private int id;
#JsonProperty("instructions")
#Valid
private List<Instructions> instructions = new ArrayList<>();
}
in Target want to ignore instructions list completely in the result but its returning empty array...how can we ignore the collections.
Output :
"id": "1245",
"instructions": []
expecting output like,
Output :
"id": "1245"
#Mapper(componentModel = "spring" , unmappedTargetPolicy = ReportingPolicy.WARN)
public interface PersonMapper {
#Mapping(target = "instructions, expression = "java(null)"")
ClassDestination map(ClassSource source);
}
attempt2:
public interface PersonMapper {
#Mapping(target = "instructions, igonore =true")
ClassDestination map(ClassSource source);
}
As others suggest to you, you obtain an empty list instead of null because it is initialized as a new ArrayList in the ClassDestination.
If you don't want to remove the initialization from ClassDestination, but still want a null value, maybe you can try:
#Mapper(componentModel = "spring" , unmappedTargetPolicy = ReportingPolicy.WARN)
public interface PersonMapper {
#Mapping(target = "instructions", ignore = true)
ClassDestination map(ClassSource source);
#AfterMapping
default void setNullValue(ClassSource source, #MappingTarget ClassDestination destination) {
destination.setInstructions(null);
}
}

How can I use spring injection, A repository class in a mapstruct mapper?

#Data
public class FilesDTO {
private int issue;
private String uniqueStr;
private StorageDomain xml;
private StorageDomain pdf;
private StorageDomain stop;
}
#Data
public class BackHalfDomain {
private int articleId;
private String uniqueStrr;
private long xmlContentId;
private long pdfContentId;
private long stopId;
private int issueNumber;
}
Using a repository class I have to fetch a StorageDomain object from the ID in BackHalfDomain. So I have to map StorageDomain object with respective fields.
like StorgeDomain sd = repo.findById(id).get(); and set this sd object in FilesDTO's xml field and so on.
This is my mapper
#Mapper(componentModel = "spring")
public interface FilesDTOMapper {
public static final FilesDTOMapper fileDTOMapper = Mappers.getMapper(FilesDTOMapper.class);
#Mapping(target = "issue", source = "domain.issueNumber")
#Mapping(target = "DOI", source = "domain.doi")
public FilesDTO map(BackHalfDomain domain);
}
I have used uses but wasn't successful.
I have used #Mapping(target="xyz", expression="java(repo.findById(id))")"
but all I got was NullPointerException
Spring injection isin't working.
Can someone have a solution for this? I am just started with mapstruct
Since mapstruct 1.2 you can use a combination of #AfterMapping and #Context.
#Mapper(componentModel="spring")
public interface FilesDTOMapper {
#Mapping(target = "xyz", ignore = true)
#Mapping(target = "issue", source = "domain.issueNumber")
#Mapping(target = "DOI", source = "domain.doi")
FilesDTO map( BackHalfDomain domain, #Context MyRepo repo);
#AfterMapping
default void map( #MappingTarget FilesDTO target, BackHalfDomain domain, #Context MyRepo repo) {
target.setXYZ(repo.findById(domain.getId()));
}
}
In 1.1 you would need to transform the mapper to a abstract class
#Mapper(unmappedTargetPolicy = org.mapstruct.ReportingPolicy.IGNORE,
componentModel = "spring",
uses = {})
public abstract class FilesDTOMapper {
#Autowired
protected MyRepo repo;
#Mapping(target = "issue", source = "domain.issueNumber")
#Mapping(target = "DOI", source = "domain.doi")
#Mapping(target="xyz", expression="java(repo.findById(domain.getId())))")
public FilesDTO map(BackHalfDomain domain);
}
I ran into this same problem. The solution was to use a Decorator as suggested in this answer. Following your code, the solution would be something like the following.
First, we have to specifiy the Decorator in the Mapper:
#Mapper(componentModel = "spring", unmappedTargetPolicy = ReportingPolicy.IGNORE)
#Primary
#DecoratedWith(FilesDTOMapperDecorator.class)
public interface FilesDTOMapper {
// basic mapping here
public FilesDTO map(BackHalfDomain domain);
}
Then, implement the Decorator:
public abstract class FilesDTOMapperDecorator implements FilesDTOMapper {
#Autowired
#Qualifier("delegate")
private FilesDTOMapper delegate;
#Autowired
private SomeRepository someRepository;
#Override
public FilesDTO map(BackHalfDomain domain) {
// Delegate basic mapping
FilesDTO filesDTO = delegate.map(domain);
// Use the repository as needed to set additional mapping
filesDTO.setSomeValue(repo.findById(id).getSomeValue());
return filesDTO;
}
}

Passing additional parameters to MapStruct mapper

Haven't figured out how to pass in additional arguments or an alternative.
Currently I'm mapping an Order and OrderLines. Both objects are different and need #mappings. Example I have an Order and OrderRequest object both are different and need #Mappings annotation to map the values, same with OrderLines.
I've created the following mappers
OrderMapper (uses = OrderLineMappers.class)
OrderLineMappers (uses = OrderLineMapper.class)
OrderLineMapper
So my issue is the OrderLine needs the OrderId from the Order object. However in the OrderLineMapper it's passing in the OrderLine and not the Order. How can I send the OrderId to the OrderLineMapper? Currently I have the OrderMapper doing an #AfterMapper, looping through the orderLines and populating with the OrderId.
Any help would be great.
Class OrderMapper
#Mapper(componentModel = "spring", uses = {OrderLineMappers.class})
public abstract class OrderMapper {
#AfterMapping
protected void orderRequestFromOrder( Order order, #MappingTarget
OrderRequest orderRequest ) {
//Wanting to do this at the OrderLineMapper class and not here
String orderId = order.getId();
List<OrderLineRequest> lines = orderRequest.getOrderLines();
List<OrderLineRequest> updatedLines = new ArrayList<>();
for (OrderLineRequest line : lines) {
line.setOrderId(orderId);
updatedLines.add(line);
}
orderRequest.setOrderLines(updatedLines);
}
#Mappings({
#Mapping( source = "orderId", target = "id" ),
#Mapping( source = "orderNumber", target = "order_number" ),
#Mapping( source = "orderLines", target = "orderLines")
})
public abstract Order orderRequestToOrder(OrderRequest orderRequest);
Class OrderLineMappers
#Mapper(componentModel = "spring", uses = {OrderLineMapper.class})
public interface OrderLineMappers {
List<OrderLine> orderLines(List<OrderLineRequest> orderLineRequest);
#InheritInverseConfiguration
List<OrderLineRequest> orderLineRequests(List<OrderLine> orderLine);
}
Class OrderLineMapper
#Mapper(componentModel = "spring")
public abstract class OrderLineMapper {
#Mappings({
#Mapping( target = "orderId", source = "orderLineId" ),
#Mapping( target = "order_line_number", source = "orderLineNumber")
})
public abstract OrderLine orderLineRequestToOrderLine(OrderLineRequest orderLineRequest);
}
Again just trying to pass in the OrderId to the OrderLineMapper. Not sure how to do this.
You can't really do this. What you can do instead is to use the #Context and perform your logic in it. You can have #AfterMapping and #BeforeMapping in your context where you can store the Order id and use that during its execution
e.g.
public class OrderMappingContext {
protected String currentOrderId;
#BeforeMapping
public void startMappingOrder(#MappingTarget Order order, OrderRequest request) {
this.currentOrderId = request.getId();
}
#AfterMapping
public void finishOrderMapping(#MappingTarget Order order) {
this.currentOrderId = null;
}
#AfterMapping
public void afterOrderLineMapping(#MappingTarget OrderLine orderLine) {
orderLine.setOrderId(this.currentOrderId);
}
}
Your mappers will then look like:
#Mapper(componentModel = "spring", uses = {OrderLineMappers.class})
public abstract class OrderMapper {
#Mapping( source = "orderId", target = "id" ),
#Mapping( source = "orderNumber", target = "order_number" ),
#Mapping( source = "orderLines", target = "orderLines")
public abstract Order orderRequestToOrder(OrderRequest orderRequest, #Context OrderMappingContext context);
#Mapper(componentModel = "spring", uses = {OrderLineMapper.class})
public interface OrderLineMappers {
List<OrderLine> orderLines(List<OrderLineRequest> orderLineRequest, #Context OrderMappingContext context);
#InheritInverseConfiguration
List<OrderLineRequest> orderLineRequests(List<OrderLine> orderLine);
}
#Mapper(componentModel = "spring")
public abstract class OrderLineMapper {
#Mapping( target = "orderId", source = "orderLineId" ),
#Mapping( target = "order_line_number", source = "orderLineNumber")
public abstract OrderLine orderLineRequestToOrderLine(OrderLineRequest orderLineRequest, #Context OrderMappingContext context);
}
On the invocation side you will do something like
orderMapper.orderRequestToOrder(request, new OrderMappingContext());

Mapping List<String> from List<Object> using mapstruct

Hi I am getting null for List action in DTO while setting it from the Child Source class using mapstruct. Could some help me in resolving this. Please find my code here
Entity Class:
public class Source {
int id;
String name;
List<ChildSource> childSource;
//getters and setters
}
public class ChildSource {
String code;
String action;
//getters and setters
}
DestinationDTO:
public class TargetDTO{
int sNo;
String mName;
List<String> actions;
//getters and setters
}
MApper Class:
#Mapper(componentModel = "spring")
public abstract class SampleMapper {
#Mappings({
#Mapping(target = "id", source = "sno"),
#Mapping(target = "name", source = "mNAme")
})
public abstract TargetDTO toDto(Source source);
#IterableMapping(elementTargetType = String.class)
protected abstract List<String> mapStringtoList(List<ChildSource> childSource);
protected String mapChildSourceToString(ChildSource child) {
return child.getAction();
}
}
But my action list is setting as null in the target dto. Could anyone help me here please?
You can do it like this.
#Mapper(componentModel = "spring")
public abstract class SampleMapper {
#Mappings({
#Mapping(target = "id", source = "sno"),
#Mapping(target = "name", source = "mNAme"),
#Mapping(target = "actions", source = "childSource")
})
public abstract TargetDTO toDto(Source source);
protected abstract List mapStringtoList(List childSource);
protected String mapChildSourceToString(ChildSource child) {
return child.getAction();
}
}
#Mapper(componentModel = "spring")
public abstract class SampleMapper {
#Mappings({
#Mapping(target = "id", source = "sno"),
#Mapping(target = "name", source = "mNAme"),
#Mapping(target = "actions", source = "childSource")
})
public abstract TargetDTO toDto(Source source);
default String mapChildSourceToString(ChildSource child) {
return child.getAction();
}
}

Categories