Как составить карту коллекций в Dozer - PullRequest
23 голосов
/ 31 августа 2009

Я бы хотел сделать что-то вроде:

ArrayList<CustomObject> objects = new ArrayList<CustomObject>();
...
DozerBeanMapper MAPPER = new DozerBeanMapper();
...
ArrayList<NewObject> newObjects = MAPPER.map(objects, ...); 

Предполагая, что:

<mapping>
  <class-a>com.me.CustomObject</class-a>
  <class-b>com.me.NewObject</class-b>   
    <field>  
      <a>id</a>  
      <b>id2</b>  
    </field>  
</mapping>

Я пытался:

ArrayList<NewObject> holder = new ArrayList<NewObject>();
MAPPER.map(objects, holder);

но объект-держатель пуст. Я также безуспешно играл со вторым аргументом ...

Ответы [ 8 ]

31 голосов
/ 31 августа 2009

Цитировать:

"Вложенные коллекции обрабатываются автоматически, но вы правы что коллекции верхнего уровня должны быть перебрал В настоящее время нет более элегантный способ справиться с этим. "

Кто-то придумал, как это сделать без использования циклической конструкции в вашей кодовой базе , но я думаю, что это просто (и более читаемо / поддерживаемо) поместить в ваш код. Надеюсь, они добавят эту способность раньше, чем позже.

9 голосов
/ 28 декабря 2011

Я столкнулся с подобной проблемой и решил использовать универсальный служебный метод, чтобы избежать итерации каждый раз, когда мне нужно выполнить такое отображение.

public static <T, U> List<U> map(final Mapper mapper, final List<T> source, final Class<U> destType) {

    final List<U> dest = new ArrayList<>();

    for (T element : source) {
        dest.add(mapper.map(element, destType));
    }

    return dest;
}

Использование тогда будет примерно таким:

    final List<CustomObject> accounts..... 
    final List<NewObject> actual = Util.map(mapper, accounts, NewObject.class);

Возможно, это можно еще упростить.

5 голосов
/ 31 августа 2009

Что происходит, так это то, что вы получаете укусы по типу стирания. Во время выполнения Java видит только ArrayList.class. Типов CustomObject и NewObject нет, поэтому Dozer пытается сопоставить java.util.ArrayList, а не ваш CustomObject с NewObject.

Что должно работать (полностью не проверено):

List<CustomObject> ori = new ArrayList<CustomObject>();
List<NewObject> n = new ArrayList<NewObject>();
for (CustomObject co : ori) {
    n.add(MAPPER.map(co, CustomObject.class));
}
3 голосов
/ 16 апреля 2014

вы можете сделать это так:

public <T,S> List<T> mapListObjectToListNewObject(List<S> objects, Class<T> newObjectClass) {
final List<T> newObjects = new ArrayList<T>();
for (S s : objects) {
    newObjects.add(mapper.map(s, newObjectClass));
}
return newObjects;

}

и используйте его:

ArrayList<CustomObject> objects = ....
List<NewObject> newObjects = mapListObjectToListNewObject(objects,NewObject.class);
2 голосов
/ 18 сентября 2017

Я сделал это, используя Java 8 и dozer 5.5. Вам не нужно никаких файлов XML для отображения. Вы можете сделать это на Java.

Вам не нужно никаких дополнительных сопоставлений для списков , единственное, что вам нужно, это

необходимо добавить список в качестве поля в отображении

. См. Пример конфигурации бина ниже.

Класс конфигурации пружины

@Configuration
public class Config {

@Bean
    public DozerBeanMapper dozerBeanMapper() throws Exception {
        DozerBeanMapper mapper = new DozerBeanMapper();
        mapper.addMapping( new BeanMappingBuilder() {
            @Override
            protected void configure() {
                mapping(Answer.class, AnswerDTO.class);
                mapping(QuestionAndAnswer.class, QuestionAndAnswerDTO.class).fields("answers", "answers");                  
            }
        });
        return mapper;
    }

}

// Класс ответа и классы AnswerDTO имеют одинаковые атрибуты

public class AnswerDTO {

    public AnswerDTO() {
        super();
    }

    protected int id;
    protected String value;

    //setters and getters
}

// у класса QuestionAndAnswerDTO есть список ответов

public class QuestionAndAnswerDTO {

    protected String question;
    protected List<AnswerDTO> answers;

   //setters and getters
}

// ПОЗВОЛЯЕМ классу QuestionAndAnswer иметь поля, аналогичные QuestionAndAnswerDTO

// Затем, чтобы использовать маппер в вашем коде, автоматически подключить его

@Autowired
private DozerBeanMapper dozerBeanMapper;
// in your method


 QuestionAndAnswerDTO questionAndAnswerDTO =
    dozerBeanMapper.map(questionAndAnswer, QuestionAndAnswerDTO.class);

Надеюсь, это поможет кому-то следовать подходу Java вместо XML.

2 голосов
/ 09 мая 2011

Для этого случая использования я однажды написал небольшой вспомогательный класс:

import java.util.Collection;

/**
 * Helper class for wrapping top level collections in dozer mappings.
 * 
 * @author Michael Ebert
 * @param <E>
 */
public final class TopLevelCollectionWrapper<E> {

    private final Collection<E> collection;

    /**
     * Private constructor. Create new instances via {@link #of(Collection)}.
     * 
     * @see {@link #of(Collection)}
     * @param collection
     */
    private TopLevelCollectionWrapper(final Collection<E> collection) {
        this.collection = collection;
    }

    /**
     * @return the wrapped collection
     */
    public Collection<E> getCollection() {
        return collection;
    }

    /**
     * Create new instance of {@link TopLevelCollectionWrapper}.
     * 
     * @param <E>
     *            Generic type of {@link Collection} element.
     * @param collection
     *            {@link Collection}
     * @return {@link TopLevelCollectionWrapper}
     */
    public static <E> TopLevelCollectionWrapper<E> of(final Collection<E> collection) {
        return new TopLevelCollectionWrapper<E>(collection);
    }
}

Затем вы бы назвали dozer следующим образом:

private Mapper mapper;

@SuppressWarnings("unchecked")
public Collection<MappedType> getMappedCollection(final Collection<SourceType> collection) {
    TopLevelCollectionWrapper<MappedType> wrapper = mapper.map(
            TopLevelCollectionWrapper.of(collection),
            TopLevelCollectionWrapper.class);

    return wrapper.getCollection();
}

Единственный недостаток: вы получаете предупреждение «unchecked» на mapper.map(...) из-за того, что интерфейс Dozers Mapper не обрабатывает универсальные типы.

1 голос
/ 25 мая 2016

Вы можете реализовать свой собственный класс mapper, который расширит dozer mapper. Пример: Создайте интерфейс, который добавляет дополнительный метод в dozer mapper:

public interface Mapper extends org.dozer.Mapper {
    <T> List<T> mapAsList(Iterable<?> sources, Class<T> destinationClass);
}

Следующий шаг: напишите свой собственный класс Mapper, реализовав вышеуказанный интерфейс.

добавьте следующий метод в ваш класс реализации:

public class MyMapper implements Mapper {
    @Override
    public <T> List<T> mapAsList(Iterable<?> sources, Class<T> destinationClass) {
        //can add validation methods to check if the object is iterable
        ArrayList<T> targets = new ArrayList<T>();
        for (Object source : sources) {
            targets.add(map(source, destinationClass));
        }
        return targets;
    }
    //other overridden methods.
}

Надеюсь, это поможет

1 голос
/ 19 апреля 2012

Не совсем улучшение, больше похоже на синтаксический сахар, которого можно достичь благодаря Гуаве (и, скорее всего, подобное возможно с Apache Commons ):

final List<MyPojo> mapped = Lists.newArrayList(Iterables.transform(inputList, new Function<MyEntity, MyPojo>() {
    @Override public MyPojo apply(final MyEntity arg) {
        return mapper.map(arg, MyPojo.class);
    }
}));

Это также можно превратить в универсальную функцию - как предлагается в других ответах.

...