如何在Dozer中映射集合

26

我想要做类似于以下的事情:

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个回答

32

1
线程的地址已更改为:http://sourceforge.net/projects/dozer/forums/forum/452530/topic/3017095(以防他们决定取消自动重定向)。 - Mark
线程的地址已失效,如果能添加一些替代方案将不胜感激。 - Jabir

12

我遇到了类似的问题,决定使用通用的实用方法来避免每次需要执行此映射时进行迭代。

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
发生的情况是您受到了类型擦除的影响。在运行时,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));
}

除了逐个映射每个对象之外,你还有什么其他方法可以做到这一点? - Stephane Grenier
除非Dozer支持集合操作(我不太熟悉它的API),否则我看不到其他方法,只能使用循环。 - Yishai
这就是问题所在。我相信它应该支持一种不需要循环的方式,只是我找不到具体的细节。 - Stephane Grenier

3

您可以这样做:

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

我已经用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;
    }

}

//Answer类和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
您可以实现自己的映射器类,该类将扩展dozer映射器。 例如: 创建一个接口,该接口将向dozer映射器添加附加方法:
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.
}

希望这能帮到您。

2

这并不是真正的改进,更像是一种语法糖,可以通过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);
    }
}));

这也可以转化为通用函数 - 正如其他答案所建议的那样。

2

针对这种用例,我曾经编写了一个小助手类:

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:

然后您将以以下方式调用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();
}

唯一的缺点:由于Dozer Mapper接口不能处理泛型类型,所以在mapper.map(...)上你会收到一个“unchecked”警告。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接