Map Iterable to Object with MapStruct
In Java, working with Iterable
types like lists are common, but sometimes we need to map a collection of objects (such as a list of entities) to a single non-iterable object (like a DTO or summary object). This is where MapStruct comes in. MapStruct is a Java annotation-based mapper that simplifies the process of converting between different types, including iterable to non-iterable transformations. Let us delve into understanding how to use Java MapStruct for mapping an Iterable to an object.
1. Understand the Use Case
Imagine you have a list of Product
entities and you need to map it to a summary object, such as a ProductSummaryDTO
, containing an aggregate of information from the list. The challenge here is converting the list (an iterable type) into a single object (a non-iterable type). Let’s take an example:
- We have a list of products with
price
andquantity
fields. - We want to map it to a single object,
ProductSummaryDTO
, which contains total price and total quantity.
2. Map Iterable to Non-Iterable Type
MapStruct doesn’t directly support converting iterables to single objects out of the box, but we can implement custom mapping methods to achieve this. By using MapStruct’s @Mapper
annotation and custom mapping logic, we can transform iterables into objects.
2.1 Solution
Let’s go through the process step-by-step:
2.1.1 Create Product Entity
The class is a simple Java class (also known as POJO – Plain Old Java Objects) that models a product with two attributes – price
and quantity
.
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 | public class Product { private double price; private int quantity; // Constructors, Getters, and Setters public Product( double price, int quantity) { this .price = price; this .quantity = quantity; } public double getPrice() { return price; } public int getQuantity() { return quantity; } } |
2.1.2 Create ProductSummaryDTO
The ProductSummaryDTO
class is a DTO class that represents a summary of multiple products. It contains two key pieces of information:
- totalPrice: The total price of all products.
- totalQuantity: The total quantity of products.
This class is commonly used when we need to represent an aggregate view of product data (such as the combined price and quantity of a list of products).
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 | public class ProductSummaryDTO { private double totalPrice; private int totalQuantity; // Constructors, Getters, and Setters public ProductSummaryDTO( double totalPrice, int totalQuantity) { this .totalPrice = totalPrice; this .totalQuantity = totalQuantity; } public double getTotalPrice() { return totalPrice; } public int getTotalQuantity() { return totalQuantity; } } |
2.1.3 Custom Mapping Logic using MapStruct
We will use MapStruct to write a custom mapper method that will take a list of Product
objects and map them to ProductSummaryDTO
.
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 | import org.mapstruct.Mapper; import org.mapstruct.Mapping; import org.mapstruct.factory.Mappers; @Mapper public interface ProductMapper { ProductMapper INSTANCE = Mappers.getMapper(ProductMapper. class ); default ProductSummaryDTO mapToProductSummary(Iterable<Product> products) { double totalPrice = 0 ; int totalQuantity = 0 ; for (Product product : products) { totalPrice += product.getPrice(); totalQuantity += product.getQuantity(); } return new ProductSummaryDTO(totalPrice, totalQuantity); } } |
The code defines a:
@Mapper
: This is the MapStruct annotation that marks the interface as a mapper.mapToProductSummary
: This is the custom mapping method that iterates through theIterable<Product>
and aggregates the total price and quantity.- Inside the method, we loop through each product, sum the prices and quantities, and return a new
ProductSummaryDTO
with the results.
2.1.4 Code Output
Now let’s test the mapper:
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 | import java.util.Arrays; import java.util.List; public class Main { public static void main(String[] args) { List<Product> productList = Arrays.asList( new Product( 10.5 , 2 ), new Product( 20.0 , 3 ), new Product( 5.5 , 1 ) ); ProductSummaryDTO summary = ProductMapper.INSTANCE.mapToProductSummary(productList); System.out.println( "Total Price: " + summary.getTotalPrice()); System.out.println( "Total Quantity: " + summary.getTotalQuantity()); } } |
When the code is executed the following output will be logged on the IDE console.
1 2 | Total Price: 36.0 Total Quantity: 6 |
3. Conclusion
In this article, we demonstrated how to use MapStruct to map an Iterable
(a list of products) to a single object (a summary DTO). While MapStruct doesn’t natively support this out-of-the-box, by writing custom mapping methods, we can easily handle complex mappings. This solution is particularly useful for scenarios where we need to aggregate data from collections into a single object. MapStruct’s flexibility and ease of use make it a powerful tool for transforming data between different types in a clean, maintainable way.