Loren
Loren

Reputation: 340

MapStruct - Map list of objects in single object

I am getting a list of objects from 3rd party but it will always contain one object only. So at my end in target I have created it as an object rather than list. That object contains multiple lists inside it just like source object.

This is how I am trying to map a list to an object. ChargeTransaction contain orderInvoice as an object and not a list. For list which are inside ChargeTransaction I have created separate mappers. I dont want to write java code in @afterMapping because then how nested lists will be mapped. The nested lists are of type in both the objects.

@Mapping(target = "orderInvoice", source = "basePaymentRequest.invoice.eventPayload.orderInvoices")
ChargeTransaction createInvoiceCTMapper(PaymentTriggerBaseModel basePaymentRequest, ChargeType chargeType);

Error

java: Can't map property "List<OrderInvoice> basePaymentRequest.invoice.eventPayload.orderInvoices" to "OrderInvoice orderInvoice". Consider to declare/implement a mapping method: "OrderInvoice map(List<OrderInvoice> value)".

I tried

@Mapping(target = "orderInvoice", expression= "java(basePaymentRequest.invoice.eventPayload.orderInvoices.get(0))")

But it gives error in Impl class

 chargeTransaction.setOrderInvoice( basePaymentRequest.invoice.eventPayload.orderInvoices.get(0) );

 java: incompatible types: com.sams.oms.ng.common.models.payment.request.OrderInvoice cannot be converted to com.sams.oms.ng.common.models.payment.cosmos.OrderInvoice

Upvotes: 1

Views: 13313

Answers (3)

swdEagle
swdEagle

Reputation: 1

The first answer provided is correct, but you do not need to use the qualifiedByName attribute and @Named annotation in this case. Mapstruct is smart enough to pick up your method itself if there is only one possible candidate to map a collection of OrderInvoice to one single OrderInvoice.

I prefer to leave those out to make the code a bit cleaner.

In general, you typically only need qualifiedByName when you overwrite a standard mapstruct conversion (like String to String, but your custom function isn't a straight copy) or when multiple conversions of the same type exist.

The 2nd answer is risky, as there are no null checks and any of those "get" calls could result in a null being returned and then doing a get on that will result in NPE.

Upvotes: 0

Abhilash Galipally
Abhilash Galipally

Reputation: 24

If we are mapping with the Java expression we need to add get and () in the expression like below.

@Mapping(target = "orderInvoice", expression= "java(basePaymentRequest.getInvoice().getEventPayload().getOrderInvoices().get(0))")

Upvotes: 0

Luca Basso Ricci
Luca Basso Ricci

Reputation: 18403

IMHO the best way to solve this problem is to use a @Named paired with @Mapping#qualifiedByName

@Mapper
class Mapper {

  @Mapping(target = "orderInvoice", source ="basePaymentRequest.invoice.eventPayload.orderInvoices", qualifiedByName="firstElement")
  ChargeTransaction createInvoiceCTMapper(PaymentTriggerBaseModel basePaymentRequest, ChargeType chargeType);

  @Named("firstElement")
  OrderInvoice map(List<OrderInvoice> value) {
    if(value == null) return null;
    if(value.isEmpty()) return null;
    return map(value.get(0));
  }

  abstract com.sams.oms.ng.common.models.payment.request.OrderInvoice map(com.sams.oms.ng.common.models.payment.cosmos.OrderInvoice invoice);
}

In this way you are instructed MapStruct to use map(List<>) to convert invoices to a single OrderInvoice and abstract map(OrderInvoice) to let MapStruct autogenerate mapping code.

Code in untested because I haven't limited spare time today,but I hope my example may be useful;if anything is wrong feel free to comment and I will correct code asap.

Upvotes: 3

Related Questions