映射嵌套元素 – Mapstruct

huangapple go评论83阅读模式
英文:

Map Nested elements - Mapstruct

问题

我正试图使用MapStruct将以下源类映射到目标类。

目标类:

public class Response {
    private List<Customer> customer = new ArrayList<Customer>();
}

public class Customer {
    private String customerId;
    private List<Product> products = new ArrayList<Product>();
}

public class CustProduct {
    private String CustProductId;
    private String CustPdtName;
    private List<productDetail> CustProductDetails = new ArrayList<productDetail>();
}

源类:

public class UserList {
    protected List<User> user;
}

public class User {
    protected String userId;
    protected List<String> productRefId;  //该特定用户的产品列表
}

public class ProductList {
    protected List<Product> product;
}

public class Product {
    protected String productId;       //对productRefId的引用
    protected String productName;
    protected List<Details> productDetails;
}

映射器接口:

List<Customer> mapUser(List<User> user);

@Mappings({
        @Mapping(target = "customerId", source = "userId"),
        @Mapping(target = "products", ignore = true)
})
Customer mapUser(User user);

@Mappings({
        @Mapping(target = "CustProductId", source = "productId"),
        @Mapping(target = "CustPdtName", source = "productName"),
        @Mapping(target = "CustProductDetails", source = "productDetails")
})
CustProduct mapUser(Product product);

我的问题是,我想要将 CustProduct 连接到 Customer
为此,我尝试了像下面这样的 AfterMapping:

default void findProducts(User user, @MappingTarget Customer customer) {
    List<String> productIds = user.getProductRefId();
    List<CustProduct> custProducts = new ArrayList<>();
    for (int i = 0; i < productIds.size(); i++) {
        CustProduct custProduct = new CustProduct();
        custProduct.setCustProductId(productIds.get(i));
        // 在此处设置 custProduct 对象的 productName 和 productDetails(通过迭代 ProductList 并从 Product 中获取)
        custProducts.add(custProduct);
    }
    customer.setCustProducts(custProducts);
}

有人能否请帮忙填写上面的注释部分?
或者是否有其他选项可以映射这些对象?

编辑:我尝试了下面的解决方案,但接口实现类本身发生了变化。

英文:

I'm trying to map following source classes to target class using MapStruct.

Target Classes :

public class Response {
    private List&lt;Customer&gt; customer = new ArrayList&lt;Customer&gt;();
}

public class Customer {
    private String customerId;
    private List&lt;Product&gt; products = new ArrayList&lt;Product&gt;();
}

public class CustProduct {
    private String CustProductId;
    private String CustPdtName;
    private List&lt;productDetail&gt; CustProductDetails = new ArrayList&lt;productDetail&gt;();
}

Source Classes :

public class UserList {
    protected List&lt;User&gt; user;
}

public class User {
    protected String userId;
    protected List&lt;String&gt; productRefId;  //List of products for that particular user
}

public class ProductList {
    protected List&lt;Product&gt; product;
}
public class Product {
   protected String productId;       //Reference to productRefId
   protected String productName;
   protected List&lt;Details&gt; productDetails;
}

Mapper Interface :

 List&lt;Customer&gt; mapUser(List&lt;User&gt; user);

    @Mappings({
            @Mapping(target = &quot;customerId&quot;, source = &quot;userId”),
            @Mapping(target = &quot;products&quot;, ignore = true)
    })
    Customer mapUser(User user);

    @Mappings({
        @Mapping(target = &quot;CustProductId&quot;, source = &quot;productId&quot;),
        @Mapping(target = &quot;CustPdtName&quot;, source = &quot;productName&quot;),
        @Mapping(target = &quot;CustProductDetails&quot;, source = &quot;productDetails&quot;)
})
CustProduct mapUser(Product product);

My problem is, I want to connect CustProduct with Customer
For that, I tried AfterMapping like :

default void findProducts(User user, @MappingTarget Customer customer) {
            List&lt;String&gt; productIds = user.getproductRefId();
            List&lt;CustProduct&gt; custProducts = new ArrayList&lt;&gt;();
            for(int i=0; i&lt;productIds.size();i++){
                    CustProduct custProduct = new CustProduct();
                    custProduct.setCustProductId(productIds.get(i));
                    //Here I want set productName and productDetails to custProduct Object(Iterating through ProductList and get from Product)
                    custProducts.add(custProduct);
                }
            }
            customer.setCustProducts(custProducts);
        }

Can anyone please help to fill out the comment section above?
Or is there any other option to map these objects?

Edited : I tried the below solution but the interface implementation class itself changed.

答案1

得分: 5

你需要使用@Context注解将ProductList对象带入上下文。

将映射器方法更改为以下定义,并在调用mapUser时传递ProductList对象:

@Mappings({
    @Mapping(target = "customerId", source = "paxJourneyType.paxJourneyID"),
    @Mapping(target = "products", ignore = true)
})
Customer mapUser(User user, @Context ProductList productList);

然后您可以在@AfterMapping方法中使用相同的ProductList对象:

default void findProducts(User user, @Context ProductList productList, @MappingTarget Customer customer) {
    List<String> productIds = user.getproductRefId();
    List<CustProduct> custProducts = new ArrayList<>();
    for (int i = 0; i < productIds.size(); i++) {
        CustProduct custProduct = new CustProduct();
        custProduct.setCustProductId(productIds.get(i));
        Product product = getProduct(productList, productIds.get(i));
        custProduct.setCustPdtName(product.getProductName());
        custProducts.add(custProduct);
    }
    customer.setCustProducts(custProducts);
}

private Product getProduct(ProductList productList, String productId) {
    //在ProductList中进行迭代并从中获取Product
}
英文:

You need to use @Context annotation to bring ProductList object into the context.

Change the mapper method to below definition and pass ProductList object when calling mapUser:

@Mappings({
            @Mapping(target = &quot;customerId&quot;, source = &quot;paxJourneyType.paxJourneyID”),
            @Mapping(target = &quot;products&quot;, ignore = true)
    })
    Customer mapUser(User user, @Context ProductList productList);

and then you can use the same ProductList object in @AfterMapping method :

default void findProducts(User user, @Context ProductList productList @MappingTarget Customer customer) {
            List&lt;String&gt; productIds = user.getproductRefId();
            List&lt;CustProduct&gt; custProducts = new ArrayList&lt;&gt;();
            for(int i=0; i&lt;productIds.size();i++){
                    CustProduct custProduct = new CustProduct();
                    custProduct.setCustProductId(productIds.get(i));
                    Product product = getProduct(ProductList productList,productIds.get(i));
                    custProduct.setCustPdtName(product.getProductName);
                    custProducts.add(custProduct);
                }
            }
            customer.setCustProducts(custProducts);
        }

private Product getProduct(ProductList productList,String productId){
    //Iterate through ProductList and get from Product
}

答案2

得分: 3

你可以在没有使用 `@AfterMapping` 的情况下完成但是你需要稍微帮助 MapStruct

```java
@Mapper
public interface CustMapper {

    @Mapping(target = "customerId", source = "userId")
    @Mapping(target = "products", source = "productRefIds")
    Customer map(User user, @Context Map<String, Product> productsMap);

    List<CustProduct> map(List<String> productRefIds, @Context Map<String, Product> productsMap);

    default CustProduct map(String productId, @Context Map<String, Product> productsMap) {
        return map(productsMap.get(productId));
    }

    @Mapping(target = "custProductId", source = "productId")
    @Mapping(target = "custProductName", source = "productName")
    @Mapping(target = "custProductDetails", source = "productDetails")
    CustProduct map(Product product);

    CustProductDetail map(ProductDetail productDetail);
}

或者,你可以手动迭代 productRefIds

@Mapper
public interface CustMapper {

    @Mapping(target = "customerId", source = "userId")
    @Mapping(target = "products", source = "productRefIds")
    Customer map(User user, @Context Map<String, Product> productsMap);

    default List<CustProduct> map(List<String> productRefIds, @Context Map<String, Product> productsMap) {
        return productRefIds.stream().map(productsMap::get).map(this::map).collect(Collectors.toList());
    }

    @Mapping(target = "custProductId", source = "productId")
    @Mapping(target = "custProductName", source = "productName")
    @Mapping(target = "custProductDetails", source = "productDetails")
    CustProduct map(Product product);

    CustProductDetail map(ProductDetail productDetail);
}

在这两种情况下,你都需要以某种方式处理 productIdproductsMap 中不存在的情况。

不使用 @AfterMapping 的优点是目标类可以是不可变的。


<details>
<summary>英文:</summary>

You can do it without `@AfterMapping` but you will need to help MapStruct a little bit:

```java
@Mapper
public interface CustMapper {

    @Mapping(target = &quot;customerId&quot;, source = &quot;userId&quot;)
    @Mapping(target = &quot;products&quot;, source = &quot;productRefIds&quot;)
    Customer map(User user, @Context Map&lt;String, Product&gt; productsMap);

    List&lt;CustProduct&gt; map(List&lt;String&gt; productRefIds, @Context Map&lt;String, Product&gt; productsMap);

    default CustProduct map(String productId, @Context Map&lt;String, Product&gt; productsMap) {
        return map(productsMap.get(productId));
    }

    @Mapping(target = &quot;custProductId&quot;, source = &quot;productId&quot;)
    @Mapping(target = &quot;custProductName&quot;, source = &quot;productName&quot;)
    @Mapping(target = &quot;custProductDetails&quot;, source = &quot;productDetails&quot;)
    CustProduct map(Product product);

    CustProductDetail map(ProductDetail productDetail);
}

alternatively, you can iterate over productRefIds manually:

@Mapper
public interface CustMapper {

    @Mapping(target = &quot;customerId&quot;, source = &quot;userId&quot;)
    @Mapping(target = &quot;products&quot;, source = &quot;productRefIds&quot;)
    Customer map(User user, @Context Map&lt;String, Product&gt; productsMap);

    default List&lt;CustProduct&gt; map(List&lt;String&gt; productRefIds, @Context Map&lt;String, Product&gt; productsMap) {
        return productRefIds.stream().map(productsMap::get).map(this::map).collect(Collectors.toList());
    }

    @Mapping(target = &quot;custProductId&quot;, source = &quot;productId&quot;)
    @Mapping(target = &quot;custProductName&quot;, source = &quot;productName&quot;)
    @Mapping(target = &quot;custProductDetails&quot;, source = &quot;productDetails&quot;)
    CustProduct map(Product product);

    CustProductDetail map(ProductDetail productDetail);
}

In both scenarios you will need to handle somehow the situation, when productId is not present in the productsMap.

The advantage of not using @AfterMapping is that target classes can be immutable.

答案3

得分: 0

你的 @AfterMapping 方法不起作用,因为 @MappingTarget 应该是 builder 类型的。

@AfterMapping
default void findProducts(User user, @MappingTarget Customer.CustomerBuilder customer) {
...
}
英文:

Your @AfterMapping method doesn't works because the @MappingTarget should be of builder type

@AfterMapping
default void findProducts(User user, @MappingTarget Customer.CustomerBuilder customer) {
...
}

huangapple
  • 本文由 发表于 2020年7月27日 00:51:10
  • 转载请务必保留本文链接:https://go.coder-hub.com/63103056.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定