Convertir une Map <String, String> en POJO


186

J'ai regardé Jackson, mais il semble que je devrais convertir la carte en JSON, puis le JSON résultant en POJO.

Existe-t-il un moyen de convertir directement une carte en POJO?

Réponses:


355

Eh bien, vous pouvez aussi y parvenir avec Jackson. (et cela semble plus confortable puisque vous envisagiez d'utiliser jackson).

Utilisez ObjectMapperla convertValueméthode de:

final ObjectMapper mapper = new ObjectMapper(); // jackson's objectmapper
final MyPojo pojo = mapper.convertValue(map, MyPojo.class);

Pas besoin de convertir en chaîne JSON ou autre chose; la conversion directe est beaucoup plus rapide.


8
Vous devez inclure cette bibliothèque pour utiliser ObjectMappercompile 'com.fasterxml.jackson.core:jackson-databind:2.7.3'
Shajeel Afzal

5
L'utilisation de convertValue est la bonne réponse, mais ne créez pas d'instance ObjectMapper à chaque fois. Il est coûteux à créer et sûr pour les threads, alors créez-en un et mettez-le en cache quelque part.
glade

1
savez-vous comment faire le contraire - ou comment convertir un objet en Map <String, Object>?
anon58192932 du

2
@RaduSimionescu avez-vous compris comment convertir en profondeur des objets avec des cartes / listes imbriquées en une Map<String, Object>instance?
anon58192932

@ anon58192932 cela fonctionne si vous suivez cette réponse. J'avais juste affaire à des objets étranges qui modélisaient des listes sous forme de cartes et lorsque la sérialisation donnait des résultats inattendus. mais c'était un autre problème, rien à voir avec jackson
Radu Simionescu

60

Une solution avec Gson :

Gson gson = new Gson();
JsonElement jsonElement = gson.toJsonTree(map);
MyPojo pojo = gson.fromJson(jsonElement, MyPojo.class);

1
quel sera le vice-versa
Prabs

2
@Prabs - Le vice-versa serait gson.toJson ()
AlikElzin-kilaka

Pas besoin de convertir la carte en json. map.toString () suffit. Gson gson = nouveau Gson (); MyPojo pojo = gson.fromJson (map.toString (), MyPojo.class);
Esakkiappan .E

1
@ Esakkiappan.E, pourquoi pensez-vous map.toString()fournir la chaîne correcte? Une implémentation de toString()ne garantit pas un format spécifique.
AlikElzin-kilaka le

4

Oui, il est certainement possible d'éviter la conversion intermédiaire en JSON. En utilisant un outil de copie profonde comme Dozer, vous pouvez convertir la carte directement en POJO. Voici un exemple simpliste:

Exemple POJO:

public class MyPojo implements Serializable {
    private static final long serialVersionUID = 1L;

    private String id;
    private String name;
    private Integer age;
    private Double savings;

    public MyPojo() {
        super();
    }

    // Getters/setters

    @Override
    public String toString() {
        return String.format(
                "MyPojo[id = %s, name = %s, age = %s, savings = %s]", getId(),
                getName(), getAge(), getSavings());
    }
}

Exemple de code de conversion:

public class CopyTest {
    @Test
    public void testCopyMapToPOJO() throws Exception {
        final Map<String, String> map = new HashMap<String, String>(4);
        map.put("id", "5");
        map.put("name", "Bob");
        map.put("age", "23");
        map.put("savings", "2500.39");
        map.put("extra", "foo");

        final DozerBeanMapper mapper = new DozerBeanMapper();
        final MyPojo pojo = mapper.map(map, MyPojo.class);
        System.out.println(pojo);
    }
}

Production:

MyPojo [id = 5, nom = Bob, âge = 23, économies = 2500,39]

Remarque: Si vous modifiez votre carte source en a, Map<String, Object>vous pouvez copier des propriétés imbriquées arbitrairement profondes (avec Map<String, String>vous n'obtenez qu'un seul niveau).


1
Comment pouvez-vous faire une "copie complète" de Map vers POJO? Disons par exemple que vous avez un User.class qui encapsule un Address.class et que la carte a une clé comme "address.city", "address.zip" et ceux-ci doivent être mappés à User.Address.City et User.Address.Zip ? Il ne semble pas interpréter automatiquement le point dans la clé Map comme un sous-niveau du graphique d'objet.
szxnyc

4

si vous avez des types génériques dans votre classe, vous devez utiliser TypeReferenceavec convertValue().

final ObjectMapper mapper = new ObjectMapper();
final MyPojo<MyGenericType> pojo = mapper.convertValue(map, new TypeReference<MyPojo<MyGenericType>>() {});

Vous pouvez également l'utiliser pour convertir un pojo en java.util.Mapdos.

final ObjectMapper mapper = new ObjectMapper();
final Map<String, Object> map = mapper.convertValue(pojo, new TypeReference<Map<String, Object>>() {});

2

J'ai testé Jackson et BeanUtils et j'ai découvert que BeanUtils est beaucoup plus rapide.
Dans ma machine (Windows8.1, JDK1.7) j'ai obtenu ce résultat.

BeanUtils t2-t1 = 286
Jackson t2-t1 = 2203


public class MainMapToPOJO {

public static final int LOOP_MAX_COUNT = 1000;

public static void main(String[] args) {
    Map<String, Object> map = new HashMap<>();
    map.put("success", true);
    map.put("data", "testString");

    runBeanUtilsPopulate(map);

    runJacksonMapper(map);
}

private static void runBeanUtilsPopulate(Map<String, Object> map) {
    long t1 = System.currentTimeMillis();
    for (int i = 0; i < LOOP_MAX_COUNT; i++) {
        try {
            TestClass bean = new TestClass();
            BeanUtils.populate(bean, map);
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
    }
    long t2 = System.currentTimeMillis();
    System.out.println("BeanUtils t2-t1 = " + String.valueOf(t2 - t1));
}

private static void runJacksonMapper(Map<String, Object> map) {
    long t1 = System.currentTimeMillis();
    for (int i = 0; i < LOOP_MAX_COUNT; i++) {
        ObjectMapper mapper = new ObjectMapper();
        TestClass testClass = mapper.convertValue(map, TestClass.class);
    }
    long t2 = System.currentTimeMillis();
    System.out.println("Jackson t2-t1 = " + String.valueOf(t2 - t1));
}}

5
La différence est: Jackson a tout un cadre de conversion de type avec lui. par exemple, le Mapcontient map.put("data","2016-06-26")et TestClassa un champ private LocalDate data;, alors Jackson serait capable de faire avancer les choses, tandis que BeanUtils échouera.
Benjamin M

6
J'ai entendu dire que la création d'une ObjectMapperinstance est un processus qui prend du temps et des ressources, et il est recommandé de réutiliser une instance de mappeur au lieu de la créer à nouveau à chaque fois. Je pense qu'il serait préférable de le sortir du test lop
Mixaz

3
pas un test juste, puisque BeanUtils est capable de mettre en cache après la première itération, alors qu'ObjectMapper n'en a jamais la chance.
Lucas Ross

1

Les réponses fournies jusqu'à présent en utilisant Jackson sont si bonnes, mais vous pouvez quand même avoir une fonction util pour vous aider à convertir différentsPOJO s comme suit:

    public static <T> T convert(Map<String, Object> aMap, Class<T> t) {
        try {
            return objectMapper
                    .convertValue(aMap, objectMapper.getTypeFactory().constructType(t));
        } catch (Exception e) {
            log.error("converting failed! aMap: {}, class: {}", getJsonString(aMap), t.getClass().getSimpleName(), e);
        }
        return null;
    }

0

Exemple de conversion de la carte en POJO. Notez que la clé de la carte contient un soulignement et que la variable de champ est hump.

User.class POJO

import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;

@Data
public class User {
    @JsonProperty("user_name")
    private String userName;
    @JsonProperty("pass_word")
    private String passWord;
}

L'App.class teste l'exemple

import java.util.HashMap;
import java.util.Map;

import com.fasterxml.jackson.databind.ObjectMapper;

public class App {
    public static void main(String[] args) {
        Map<String, String> info = new HashMap<>();
        info.put("user_name", "Q10Viking");
        info.put("pass_word", "123456");

        ObjectMapper mapper = new ObjectMapper();
        User user = mapper.convertValue(info, User.class);

        System.out.println("-------------------------------");
        System.out.println(user);
    }
}
/**output
-------------------------------
User(userName=Q10Viking, passWord=123456)
 */

0

@Hamedz si vous utilisez beaucoup de données, utilisez Jackson pour convertir des données lumineuses, utilisez apache ... TestCase:

import java.lang.reflect.InvocationTargetException; import java.util.HashMap; import java.util.Map; import org.apache.commons.beanutils.BeanUtils; import com.fasterxml.jackson.databind.ObjectMapper; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; public class TestPerf { public static final int LOOP_MAX_COUNT = 1000; public static void main(String[] args) { Map<String, Object> map = new HashMap<>(); map.put("success", true); map.put("number", 1000); map.put("longer", 1000L); map.put("doubler", 1000D); map.put("data1", "testString"); map.put("data2", "testString"); map.put("data3", "testString"); map.put("data4", "testString"); map.put("data5", "testString"); map.put("data6", "testString"); map.put("data7", "testString"); map.put("data8", "testString"); map.put("data9", "testString"); map.put("data10", "testString"); runBeanUtilsPopulate(map); runJacksonMapper(map); } private static void runBeanUtilsPopulate(Map<String, Object> map) { long t1 = System.currentTimeMillis(); for (int i = 0; i < LOOP_MAX_COUNT; i++) { try { TestClass bean = new TestClass(); BeanUtils.populate(bean, map); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } } long t2 = System.currentTimeMillis(); System.out.println("BeanUtils t2-t1 = " + String.valueOf(t2 - t1)); } private static void runJacksonMapper(Map<String, Object> map) { long t1 = System.currentTimeMillis(); for (int i = 0; i < LOOP_MAX_COUNT; i++) { ObjectMapper mapper = new ObjectMapper(); TestClass testClass = mapper.convertValue(map, TestClass.class); } long t2 = System.currentTimeMillis(); System.out.println("Jackson t2-t1 = " + String.valueOf(t2 - t1)); } @Data @AllArgsConstructor @NoArgsConstructor public static class TestClass { private Boolean success; private Integer number; private Long longer; private Double doubler; private String data1; private String data2; private String data3; private String data4; private String data5; private String data6; private String data7; private String data8; private String data9; private String data10; } }
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.