【问题标题】:Orika: Map from String to a List of SomeObjectsOrika:从字符串映射到 SomeObjects 列表
【发布时间】:2017-05-31 03:38:45
【问题描述】:

考虑以下情况:

public class A {
    private String stringA;

    public String getStringA() {
        return stringA;
    }

    public void setStringA(String stringA) {
        this.stringA = stringA;
    }

}

public class B {

    List<SomeObject> someObjects;

    public List<SomeObject> getSomeObjects() {
        if (someObjects == null) {
            someObjects = new ArrayList<SomeObject>();
        }
        return someObjects;
    }

}

public class SomeObject {
    private String stringSomeObject;

    public String getStringSomeObject() {
        return stringSomeObject;
    }

    public void setStringSomeObject(String stringSomeObject) {
        this.stringSomeObject = stringSomeObject;
    }

}

我想从A 映射到B。在映射这些时,stringA 需要映射到 SomeObject 中的 stringSomeObject。我试图为此编写一个 Orika-Mapper:

public class MyMapper extends ConfigurableMapper {

    @Override
    protected void configure(MapperFactory factory) {
        ConverterFactory converterFactory = factory.getConverterFactory();
        converterFactory.registerConverter(new StringToSomeObjectConverter());

        factory.classMap(A.class, B.class) //
                .field("stringA", "someObjects") //
                .byDefault() //
                .register();
    }

}

它将类 A 映射到 B 并且每当遇到从 StringList&lt;SomeObject&gt; 的转换时,它都会调用自定义转换器:

public class StringToSomeObjectConverter extends CustomConverter<String, List<SomeObject>> {

    private static final String BORROWER_PARTY_TYP_CODE = "147";

    @Override
    public List<SomeObject> convert(String source, Type<? extends List<SomeObject>> destinationType) {
        SomeObject someObject = new SomeObject();
        someObject.setStringSomeObject(source);
        return Arrays.asList(someObject);
    }

}

我编写了一个单元测试来确保它有效:

@Test
public void testMap() throws Exception {
    A a = new A();
    a.setStringA("a");

    B outcome = new MyMapper().map(a, B.class);

    assertThat(outcome.getSomeObjects.size(), is(1));
}

遗憾的是,这个测试失败了:

java.lang.AssertionError: 
Expected: is <1>
   but: was <0>

似乎转换器从未执行过,所以我尝试调试它。事实上:调试器永远不会到达转换器。难道我做错了什么?这好像是。我知道还有更多可以使用的方法,例如:mapAToB 例如...

好的,我找到了一个解决方案...不!这不是一个解决方案,它只是一种解决方法。我也将stringA 定义为List&lt;String&gt;,并定义了一个扩展CustomConverter&lt;String, LoanContrReqERPCrteReqLoanContrBrrwrPty&gt; 的转换器。

因为这感觉有点“hacky”,所以我仍然对一个好的解决方案感兴趣。 (虽然我只是认为这个解决方案可能很好:现在两个对象的数据结构比以前更平等。问题是,对象B 来自外部服务,我无法修改它。)

【问题讨论】:

    标签: java unit-testing converter mapper orika


    【解决方案1】:

    您的映射不起作用,因为您没有someObjects 的设置器。

    当 Orika 尝试为 mapper 生成代码时,它会检查 classMap 中的所有 fieldMaps,因为 sourceProperty 是可读的,destinationProperty 是可分配的。如果此检查通过,生成器会将字段转换放入生成的映射器中。如果检查失败,Orika 将跳过此字段转换。

    您可以使用几个选项来解决问题:

    • 您可以在B 类中为someObjects 字段添加setter:

      public static class B {
      
          List<SomeObject> someObjects;
      
          public List<SomeObject> getSomeObjects() {
              if (someObjects == null) {
                  someObjects = new ArrayList<SomeObject>();
              }
              return someObjects;
          }
      
          public void setSomeObjects(List<SomeObject> someObjects) {
              this.someObjects = someObjects;
          }
      }
      
    • 使用自定义映射器而不是转换器:

          factory.classMap(A.class, B.class)
                  .customize(
                          new CustomMapper<A, B>() {
                              @Override
                              public void mapAtoB(A a, B b, MappingContext context) {
                                  SomeObject someObject = new SomeObject();
                                  someObject.setStringSomeObject(a.getStringA());
                                  b.getSomeObjects().add(someObject);
                              }
                          }
                  )
                  .byDefault()
                  .register(); 
      

    Orika 将在解析字段映射后调用 customMapper。
    生成的映射器将如下所示:

        b.setOtherField(a.getOtherField());
        if (customMapper != null) {
            customMapper.map(source, destination); <-- Your mapper invocation
        }
    
    • 对字段使用以下语法:

          factory.classMap(A.class, B.class)
                  .field("stringA", "someObjects[0].stringSomeObject")
                  .byDefault()
                  .register();
      

    生成的映射器如下所示:

        if (source.getStringA() != null) {
            if (((((java.util.List) destination.getSomeObjects()).size() <= 0 || ((List) destination.getSomeObjects()).get(0) == null))) {
                ((java.util.List) destination.getSomeObjects()).add(0, ((BoundMapperFacade) usedMapperFacades[0]).newObject(((String) source.getStringA()), mappingContext));
            }
        }
    
        if (!(((java.lang.String) source.getStringA()) == null)) {
            (((java.util.List) destination.getSomeObjects()).get(0)).setStringSomeObject(source.getStringA());
        } else if (!(((java.util.List) destination.getSomeObjects()) == null) && !((((java.util.List) destination.getSomeObjects()).size() <= 0 || ((List) destination.getSomeObjects()).get(0) == null))) {
            ( ((java.util.List) destination.getSomeObjects()).get(0)).setStringSomeObject(null);
        }
    

    Orika 中还有一个错误,使用语法 .field("stringA", "elements{stringB}") (Incorrect mapper code generated for mapping from a single property to property of collection element) 从单个属性映射到集合的属性。错误于 2016 年 12 月 31 日在此处关闭:Fix for bug

    【讨论】:

    • 好的,还有一个问题:使用 .customize() 和自定义映射器和使用转换器有什么区别?
    • 字段的自定义转换器将被放入生成的映射器中,以防万一目标属性是可分配的(换句话说,如果您有此字段的设置器)。对于自定义映射器,生成的代码如下所示:` b.setField1(a.getField1()); if (customMapper != null) { customMapper.map(a, b) } `
    • 好的,这不能回答我的问题,但我会在另一个问题中发布。谢谢您的帮助。我现在知道如何将字符串映射到列表中,这是我最初的问题。
    • This 是结果问题的链接。
    猜你喜欢
    • 2023-03-18
    • 1970-01-01
    • 2018-04-21
    • 2017-03-22
    • 1970-01-01
    • 1970-01-01
    • 2018-08-06
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多