Как отобразить коллекции в бульдозере
-
20-09-2019 - |
Вопрос
Я бы хотел сделать что-то вроде:
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);
но объект holder пуст.Я также поиграл с изменением второго аргумента, но безуспешно...
Решение
Процитировать:
"Вложенные коллекции обрабатываются автоматически, но вы правы что коллекции верхнего уровня необходимо повторять .В настоящее время не существует более элегантного способа справиться с этим ".
Кто-то придумал способ сделать это без циклической конструкции в вашей кодовой базе, но я думаю, что просто проще (и более читабельно / ремонтопригодно) поместить это в ваш код.Надеюсь, они добавят эту способность рано или поздно.
Другие советы
Я столкнулся с аналогичной проблемой и решил использовать универсальный служебный метод, чтобы избежать повторения каждый раз, когда мне нужно выполнить такое сопоставление.
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);
Хотя, возможно, это можно еще упростить.
Происходит то, что вас укусило стирание типа.Во время выполнения Java видит только ArrayList.class
.Тип CustomObject
и NewObject
их нет, поэтому Дозер пытается нанести на карту 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));
}
вы можете сделать это так:
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);
Для этого случая я однажды написал небольшой вспомогательный класс:
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 не обрабатывает универсальные типы.
Я сделал это, используя Java 8 и dozer 5.5.Для сопоставления вам не нужны XML-файлы.Вы можете сделать это на Java.
Вам не нужно дополнительное сопоставление для списков., единственное, что тебе нужно, это
вам нужно добавить список в качестве поля в сопоставлении
.См. пример конфигурации bean-компонента ниже.
Класс конфигурации Spring
@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
}
//ПУСТЬ класс ВопросАнсвер имеет такие же поля, как и ВопросАнсверДТО
//Затем, чтобы использовать преобразователь в своем коде, выполните его автоматическое подключение
@Autowired
private DozerBeanMapper dozerBeanMapper;
// in your method
QuestionAndAnswerDTO questionAndAnswerDTO =
dozerBeanMapper.map(questionAndAnswer, QuestionAndAnswerDTO.class);
Надеюсь, это поможет кому-то использовать подход Java вместо XML.
На самом деле это не улучшение, а скорее синтаксический сахар, которого можно достичь благодаря Гуава (и, скорее всего, подобное возможно и с Апач Коммонс):
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);
}
}));
Это также можно превратить в общую функцию, как предлагается в других ответах.
Вы можете реализовать свой собственный класс картографа, который расширит картограф бульдозера.Пример:Создайте интерфейс, который добавляет дополнительный метод к сопоставителю бульдозеров:
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.
}
Надеюсь это поможет