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
.
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).
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
.
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:
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.
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.