2017-01-16 16 views
1

Рассмотрим следующую ситуацию:Orika: Карта из строки в Список 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 необходимо сопоставить с stringSomeObject в SomeObject. Я попытался написать 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 и всякий раз, когда он встречает преобразование из String в List<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, например, ...

Хорошо, я нашел solut ... nah! Это не решение, это просто обходной путь. Я также определил stringA как List<String> и определил расширение преобразователя CustomConverter<String, LoanContrReqERPCrteReqLoanContrBrrwrPty>.

Потому что это чувствует себя немного «взломанным», меня все еще интересует приятное решение. (Хотя я просто думаю, что это решение может быть прекрасным: теперь структура данных обоих объектов больше, чем раньше. Проблема заключается в том, что объект B поступает из внешней службы, я не могу ее модифицировать.)

ответ

2

Вы не работаете, потому что у вас нет настроек для someObjects.

Когда Orika пытается генерировать код для картографа, он проверяет все fieldMaps в classMap для sourceProperty чтения и destinationProperty присваиванию. Если эти проверки пройдены, генератор помещает преобразование поля в сгенерированный преобразователь. Если проверка не удалась, Orika просто пропустит это преобразование поля.

Несколько вариантов, вы можете использовать, чтобы решить проблему:

  • Вы можете добавить сеттера для someObjects поля в классе 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; 
        } 
    } 
    
  • Используйте пользовательский картографа вместо преобразователя:

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

Также было ошибка i n Orika для сопоставления из одного свойства в свойство коллекции с использованием синтаксиса .field("stringA", "elements{stringB}") (Incorrect mapper code generated for mapping from a single property to property of collection element). Ошибка была закрыта на 31 декабря 2016 года: Fix for bug

+0

Прочтите мою «Править» часть. Я не могу изменить класс B. – Chris311

+0

Хорошо, еще один вопрос: в чем разница между использованием .customize() с настраиваемым преобразователем и использованием конвертера? – Chris311

+0

Пользовательский конвертер для поля будет помещен в сгенерированный картограф только в том случае, если свойство назначения назначается (другими словами, если у вас есть сеттер для этого поля). Для пользовательского кода сгенерированного кода будет выглядеть так: ' b.setField1 (a.getField1()); if (customMapper! = Null) { customMapper.map (a, b) } ' –