Orika:从字符串映射到一些对象的列表

Orika: Map from String to a List of SomeObjects

考虑以下情况:

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();
    }

}

它将 class A 映射到 B 并且每当它遇到从 StringList<SomeObject> 的转换时它调用自定义转换器:

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<String>,并定义了一个扩展 CustomConverter<String, LoanContrReqERPCrteReqLoanContrBrrwrPty> 的转换器。

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

您的映射不起作用,因为您没有 someObjects 的 setter。

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

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

  • 您可以为 class 中的 someObjects 字段添加 setter B:

    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). Bug closed at 31 Dec 2016 here: Fix for bug

从单个 属性 映射到 属性 集合