web-dev-qa-db-fra.com

Comment mapper des collections en bulldozer

J'aimerais faire quelque chose comme:

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

En supposant:

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

J'ai essayé :

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

mais l'objet titulaire est vide. J'ai aussi joué avec changer le deuxième argument sans aucune chance ...

23
Stephane Grenier

Citer:

"Les collections imbriquées sont traitées automatiquement, mais vous avez raison que les collections de niveau supérieur doivent être itératées. Actuellement, il n'y a pas de moyen plus élégant de gérer cela."

Quelqu'un a trouvé un moyen de le faire sans une construction en boucle dans votre base de code , mais je pense que c'est simplement plus facile (et plus lisible/maintenable) de le mettre dans votre code. Espérons qu'ils ajouteront cette capacité plus tôt que la plus tard.

32
Stephane Grenier

Je suis confronté à un problème similaire et a décidé d'utiliser une méthode d'utilité générique pour éviter de itérations à chaque fois que je devais effectuer une telle cartographie.

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

L'utilisation serait alors quelque chose comme:

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

Peut-être que cela pourrait être simplifié plus loin cependant.

9
Michael-7

Ce qui se passe, c'est que vous soyez mordu par effacement de type. Au moment de l'exécution, Java ne voit que ArrayList.class. Le type de CustomObject et NewObject n'y est pas là-bas, alors que Dozzer tente de mapper un Java.util.ArrayList, pas votre CustomObject à NewObject.

Que devrait fonctionner (totalement non testé):

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

vous pouvez le faire comme ceci:

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;

}

et utilisez-le:

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

Pour ce cas d'utilisation, j'ai écrit une fois une petite classe d'assistance:

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

Vous appelez alors la bulldozer de la manière suivante:

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

Seulement inconvénient: vous obtenez un avertissement "non coché" sur mapper.map(...) à cause des dizaines d'interface mapper ne manipulant pas les types génériques.

2
ebi

Je l'ai fait en utilisant Java 8 et dozer 5.5. Vous n'avez pas besoin de fichiers XML pour la cartographie. Vous pouvez le faire en Java.

Vous n'avez pas besoin de mappage supplémentaire pour les listes, seule une chose dont vous avez besoin est

vous devez ajouter la liste sous forme de champ dans la cartographie

. Voir la configuration de haricot exemple ci-dessous.

Classe de configuration du ressort

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

}

// la classe de réponse et les classes de réponse ont les mêmes attributs

public class AnswerDTO {

    public AnswerDTO() {
        super();
    }

    protected int id;
    protected String value;

    //setters and getters
}

// questionandanswerdo classe a une liste de réponses

public class QuestionAndAnswerDTO {

    protected String question;
    protected List<AnswerDTO> answers;

   //setters and getters
}

// Laissez la classe Questifsandanswer a des domaines similaires comme Questionandanswerdto

// puis utiliser le mappeur dans votre code, la mise en automne

@Autowired
private DozerBeanMapper dozerBeanMapper;
// in your method


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

J'espère que cela aidera quelqu'un à suivre Java approche au lieu de XML.

2
Vins

Pas vraiment une amélioration, plus comme un sucre syntaxique pouvant être atteint grâce à GUAVA (et une chose la plus probable est possible avec 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);
    }
}));

Cela peut également être transformé en une fonction générique - comme suggéré dans d'autres réponses.

1
Anonymous

Vous pouvez implémenter votre propre classe de mapper qui prolongera la mappeuse du dozer. Exemple: Créez une interface qui ajoute une méthode supplémentaire au mapper Dozer:

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

Étape suivante: Écrivez votre propre classe de mapper en implémentant l'interface ci-dessus.

ajouter une méthode ci-dessous à votre classe de mise en œuvre:

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

J'espère que cela t'aides

1
Bikas Katwal