Как сопоставить коллекции в Dozer

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

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

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


person Stephane Grenier    schedule 31.08.2009    source источник


Ответы (8)


Цитировать:

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

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

person Stephane Grenier    schedule 31.08.2009
comment
Адрес темы изменился на: sourceforge.net/projects/dozer/forums. /forum/452530/topic/ (на всякий случай, если они решат отказаться от автопереадресации) - person Mark; 10.11.2011
comment
@Gnoupi Спасибо за добавление ссылки - person Stephane Grenier; 30.03.2012
comment
Адрес темы мертв, буду признателен, если можно будет добавить какой-нибудь альтернативный. - person Jabir; 06.03.2018

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

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

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

person Michael-7    schedule 28.12.2011

Происходит то, что вас кусает стирание типа. Во время выполнения 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));
}
person Yishai    schedule 31.08.2009
comment
Итак, как бы вы это сделали, кроме как в цикле, отображающем каждый объект один за другим? - person Stephane Grenier; 31.08.2009
comment
Я не вижу другого пути, кроме цикла (если только Dozer не поддерживает операции с коллекциями, я не очень знаком с его API). - person Yishai; 31.08.2009
comment
Вот в чем вопрос. Я считаю, что он должен поддерживать способ без зацикливания. Я просто не могу найти конкретные детали. - person Stephane Grenier; 31.08.2009

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

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);
person Taoufiq BOUKCHA    schedule 16.04.2014

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

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

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

person ebi    schedule 09.05.2011

На самом деле это не улучшение, а скорее синтаксический сахар, которого можно достичь благодаря Guava ( и, скорее всего, подобное возможно с 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);
    }
}));

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

person Anonymous    schedule 19.04.2012

Вы можете реализовать свой собственный класс маппера, который расширит маппер dozer. Пример: создайте интерфейс, который добавляет дополнительный метод к 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.
}

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

person Bikas Katwal    schedule 25.05.2016

Я сделал это, используя 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.

person Vins    schedule 18.09.2017