Core Java

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 and quantity 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 the Iterable<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.

Yatin Batra

An experience full-stack engineer well versed with Core Java, Spring/Springboot, MVC, Security, AOP, Frontend (Angular & React), and cloud technologies (such as AWS, GCP, Jenkins, Docker, K8).
Subscribe
Notify of
guest


This site uses Akismet to reduce spam. Learn how your comment data is processed.

0 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
Back to top button