How to map collections in Dozer

谁说我不能喝 提交于 2019-11-27 13:38:32

问题


I'd like to do something like:

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

Assuming:

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

I tried :

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

but the holder object is empty. I also played with changing the second argument without any luck...


回答1:


To quote:

"Nested collections are handled automatically, but you are correct that top level collections need to be iterated over. Currently there isn't a more elegant way to handle this."

Someone has figured a way to do it without a looping construct in your code base, but I think it's just easier (and more readable/maintainable) to put it in your code. Hopefully they'll add this ability sooner than later.




回答2:


I faced a similar issue, and decided on using a generic utility method to avoid iterating every time I needed to perform such mapping.

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

Usage would then be something like:

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

Possibly this could be simplified further though.




回答3:


What is happening is that you are getting bitten by type erasure. At runtime, java only sees an ArrayList.class. The type of CustomObject and NewObject aren't there, so Dozer is attempting to map a java.util.ArrayList, not your CustomObject to NewObject.

What should work (totally untested):

List<CustomObject> ori = new ArrayList<CustomObject>();
List<NewObject> n = new ArrayList<NewObject>();
for (CustomObject co : ori) {
    n.add(MAPPER.map(co, CustomObject.class));
}



回答4:


you can do it like this :

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;

}

and use it :

ArrayList<CustomObject> objects = ....
List<NewObject> newObjects = mapListObjectToListNewObject(objects,NewObject.class);



回答5:


For that use case I once wrote a little helper 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);
    }
}

You then would call dozer in the following manner:

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

Only drawback: You get a "unchecked" warning on mapper.map(...) because of Dozers Mapper interface not handling generic types.




回答6:


I have done it using Java 8 and dozer 5.5. You don't need any XML files for mapping. You can do it in Java.

You don't need any additional mapping for lists, only thing you need is

you need to add the list as a field in the mapping

. See the sample bean config below.

Spring configuration class

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

}

//Answer class and AnswerDTO classes have same attributes

public class AnswerDTO {

    public AnswerDTO() {
        super();
    }

    protected int id;
    protected String value;

    //setters and getters
}

//QuestionAndAnswerDTO class has a list of Answers

public class QuestionAndAnswerDTO {

    protected String question;
    protected List<AnswerDTO> answers;

   //setters and getters
}

//LET the QuestionAndAnswer class has similar fields as QuestionAndAnswerDTO

//Then to use the mapper in your code, autowire it

@Autowired
private DozerBeanMapper dozerBeanMapper;
// in your method


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

Hope this will help someone follow the Java approach instead of XML.




回答7:


Not really an improvement, more like a syntactic sugar that can be achieved thanks to Guava (and most likely similar thing is possible with 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);
    }
}));

This can also be turned into a generic function - as suggested in other answers.




回答8:


You can implement your own mapper class which will extend dozer mapper. Example: Create a interface that adds additional method to dozer mapper:

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

Next step: Write your own Mapper class by implementing above interface.

add below method to your implementation class:

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.
}

Hope this helps



来源:https://stackoverflow.com/questions/1358595/how-to-map-collections-in-dozer

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!