Konwertuj mapę <ciąg, ciąg> na POJO


185

Patrzyłem na Jacksona, ale wygląda na to, że musiałbym przekonwertować mapę na JSON, a następnie wynikowy JSON na POJO.

Czy istnieje sposób przekonwertowania mapy bezpośrednio na POJO?

Odpowiedzi:


355

Cóż, możesz to osiągnąć także z Jacksonem. (i wydaje się wygodniejsze, ponieważ rozważałeś użycie Jacksona).

Wykorzystaniu koniecznie ObjectMapper„S convertValuesposób:

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

Nie trzeba konwertować na ciąg JSON lub coś innego; bezpośrednia konwersja robi się znacznie szybciej.


8
Musisz włączyć tę bibliotekę, aby korzystać z ObjectMappercompile 'com.fasterxml.jackson.core:jackson-databind:2.7.3'
Shajeel Afzal,

5
Użycie convertValue jest właściwą odpowiedzią, ale nie twórz instancji ObjectMapper za każdym razem. Tworzenie i zabezpieczanie wątków jest drogie, więc utwórz je i zapisz w pamięci podręcznej.
polana

1
czy wiesz, jak to zrobić odwrotnie - lub jak przekonwertować obiekt na mapę <Ciąg, Obiekt>?
anon58192932,

2
@RaduSimionescu, czy wymyśliłeś, jak głęboko przekonwertować obiekty za pomocą zagnieżdżonych map / list na Map<String, Object>instancję?
anon58192932,

@ anon58192932 działa, jeśli zastosujesz się do tej odpowiedzi. Miałem tylko do czynienia z dziwnymi obiektami, które tworzyły listy jako mapy, a kiedy serializowanie przynosiło nieoczekiwane rezultaty. ale to był kolejny problem, nie mający nic wspólnego z Jacksonem
Radu Simionescu,

60

Rozwiązanie z Gsonem :

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

1
co będzie na odwrót
Prabs

2
@Prabs - Na odwrót będzie gson.toJson ()
AlikElzin-kilaka

Nie trzeba konwertować mapy na Json. wystarczy map.toString (). Gson gson = new Gson (); MyPojo pojo = gson.fromJson (map.toString (), MyPojo.class);
Esakkiappan .E

1
@ Esakkiappan.E, dlaczego według ciebie map.toString()zapewni prawidłowy ciąg znaków? Implementacja toString()nie gwarantuje określonego formatu.
AlikElzin-kilaka

4

Tak, zdecydowanie można uniknąć pośredniej konwersji na JSON. Korzystanie z narzędzia do głębokiego kopiowania, takiego jak Dozer , możesz przekonwertować mapę bezpośrednio na POJO. Oto uproszczony przykład:

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

Przykładowy kod konwersji:

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

Wynik:

MyPojo [id = 5, imię = Bob, wiek = 23, oszczędności = 2500,39]

Uwaga: Jeśli zmienisz mapę źródłową na Map<String, Object>a, możesz skopiować dowolnie zagnieżdżone właściwości (z Map<String, String>jednym poziomem).


1
Jak możesz zrobić „głęboką kopię” z mapy do POJO? Załóżmy na przykład, że masz klasę User.class, która zawiera klasę Address.class, a mapa ma klucz taki jak „address.city”, „address.zip”, a te muszą być mapowane na User.Address.City i User.Address.Zip ? Wydaje się, że nie interpretuje on automatycznie kropki w kluczu Mapa jako podpoziomu dla wykresu obiektowego.
szxnyc

3

jeśli masz ogólne typy w swojej klasie, powinieneś używać TypeReferencez convertValue().

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

Możesz także użyć tego do konwersji pojo na java.util.Mappowrót.

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

2

Testowałem zarówno Jacksona, jak i BeanUtils i odkryłem, że BeanUtils jest znacznie szybszy.
Na moim komputerze (Windows 8.1, JDK1.7) mam ten wynik.

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
Różnica polega na tym, że Jackson ma cały system konwersji typu. np. Mapzawiera map.put("data","2016-06-26")i TestClassma pole private LocalDate data;, wtedy Jackson byłby w stanie załatwić sprawę, podczas gdy BeanUtils zawiedzie.
Benjamin M.

6
Słyszałem, że tworzenie ObjectMapperinstancji jest procesem pochłaniającym czas / zasoby i zaleca się ponowne użycie jednej instancji mapowania zamiast tworzenia jej za każdym razem za każdym razem. Myślę, że lepiej byłoby wyjąć go z testu
LOP

3
nie jest to uczciwy test, ponieważ BeanUtils jest w stanie buforować po pierwszej iteracji, podczas gdy ObjectMapper nigdy nie ma takiej szansy.
Lucas Ross

1

Odpowiedzi udzielone do tej pory za pomocą Jacksona są tak dobre, ale nadal możesz mieć funkcję util, która pomoże Ci przekonwertować różne POJOs w następujący sposób:

    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

przekonwertować mapę na przykład POJO. Uwaga: klucz mapy zawiera podkreślenie, a zmienną pola jest garb.

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

App.class testuje przykład

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, jeśli korzystasz z wielu danych, użyj Jacksona do konwersji lekkich danych, użyj 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; } }
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.