首页 文章

使用Org.ModelMapper进行条件映射((condition)?get-this:get-that)

提问于
浏览
1

我试图有条件地使用一个吸气剂或另一个,取决于 Map 的根/源的条件 . A 引用 B ;但是,它(引用)被允许为null . 当引用不为null时,我想使用它的一个属性( B ) . 但是,当引用为null时,我想使用源中的属性( A ) .

它's a little business-logic' y但是我们的系统中有很多数据模型遵循这种模式 . 它'd be beneficial to use a mapper library and in my mind the logic is simple enough - it'与使用库的 Condition 非常相似 .

当我在getter中使用逻辑时,它将启动并初始化正常;但是,当 Map 实际用于映射对象时,我从Model-Mapper获得 IllegalArgumentException ,其中包含 object is not an instance of declaring class .

我没有解决问题的方法 . 它似乎更像是一次性完成,而不是if-or-else . 我有一个映射器,它首先在源上使用getter( A ) . 在下一行,我然后调用条件 Conditions.isNotNull()) 并映射 a -> a.getB().getDescription() . 所以在我看来它是如何工作的首先是使用 A 设置DTO的 description 属性 . 然后它将"overwrite"与 a.getB().getDescription() 的值,但仅当 a.getB() 不为空时 . 但是,它似乎没有那样工作 . 相反,对于 a.getB() 返回null的实例,我将 null 视为DTO的 description .

总之,我希望能够做类似以下的事情:

modelMapper.createTypeMap(A.class, FlatAB.class).addMappings(mapper -> {
    mapper.map(a -> a.getB() != null ? a.getB().getDescription() : 
            a.getDescription(), FlatAB::setDescription);
});

这里有一些示例代码来演示我面临的问题 .

import java.util.Optional;
import org.junit.Assert;
import org.junit.Test;
import org.modelmapper.ModelMapper;

public class TestMappingConditionalGettersWithModelMapper {

  @Test
  public void testConditionalGetter() {
    ModelMapper modelMapper = new ModelMapper();

    modelMapper.createTypeMap(A.class, FlatAB.class).addMappings(mapper -> {

      // mapper.map(a -> a.getB() != null ? a.getB().getDescription() : a.getDescription(), FlatAB::setDescription);

      mapper.map(a -> Optional
          .ofNullable(a.getB())
          .map(B::getDescription)
          .orElseGet(a::getDescription), FlatAB::setDescription);
    });

    // first try A with no relationship
    A a = new A();
    a.setDescription("description of A");
    FlatAB flatAB1 = modelMapper.map(a, FlatAB.class);
    Assert.assertEquals("they should equal", a.getDescription(), flatAB1.getDescription());


    // now try it WITH a relationship
    A a2 = new A();
    a2.setDescription("description of A2");
    B b = new B();
    b.setDescription("description of B");
    a2.setB(b);
    FlatAB flatAB2 = modelMapper.map(a2, FlatAB.class);
    Assert.assertEquals("they should equal", b.getDescription(), flatAB2.getDescription());
  }

}

class A {

  private String description;
  private B b;

  /*
   .....
   .....
   many other properties
   .....
   ....
   */

  public B getB() {
    return b;
  }

  public void setB(B b) {
    this.b = b;
  }

  public String getDescription() {
    return description;
  }

  public void setDescription(String description) {
    this.description = description;
  }

}

class B {

  private String description;

  /*
   .....
   .....
   many other properties
   .....
   ....
   */

  public String getDescription() {
    return description;
  }

  public void setDescription(String description) {
    this.description = description;
  }
}

2 回答

  • 0

    I popped the question over on the library's Github并得到了维护者的极好回答,Chun-Han, Hsiao .

    如果没有转换器,我们无法将多个源属性映射到目标 . 请试试:

    modelMapper.addMappings(mapper ->
        mapper.using(ctx -> ctx.getSource().getB() != null
            ? ctx.getSource().getB().getDescription()
            : ctx.getSource().getDescription())
          .map(src -> src, FlatAB::setDescription));
    

    我发现 ctx.getSource() 可能需要施法 - 但我是一种喂养这种类型的方法 .

  • 1

    如果我正确地理解了你的问题,我会像他一样

    Optional
        .ofNullable(a.getB())
        .map(B::getDescription)
        .orElseGet(a::getDescription)
    

    如果b不为空,这将产生B描述,否则为A描述 .

    UPDATE

    将上面的代码放到单独的方法中就可以了解A.getMappingDescription(),如:

    class A {
                String desc;
                B b;
                public String getMappingDescription() {
                    return Optional
                            .ofNullable(getB())
                            .map(B::getDesc)
                            .orElse(desc);
                }
    ...
    }
    

    您的映射将更简单:

    modelMapper.createTypeMap(A.class, FlatAB.class).addMapping(A::getMappingDescription, FlatAB::setDesc);
    

    更新2

    您可以简单地提取指定具体泛型类型的转换器,如:

    Converter<A, String> descriptionConverter = ctx -> ctx.getSource().getB() != null
            ? ctx.getSource().getB().getDesc()
            : ctx.getSource().getDesc();
    
    mapper.using(descriptionConverter).map(src -> src, FlatAB::setDesc);
    

    或明确表示:

    ((Converter<A, String>) ctx -> ctx.getSource().getB()..
    

相关问题