แปลง Map <String, String> เป็น POJO


186

ฉันดูที่แจ็คสัน แต่ดูเหมือนว่าฉันจะต้องแปลงแผนที่เป็น JSON และจากนั้นผลลัพธ์ JSON เป็น POJO

มีวิธีแปลงแผนที่โดยตรงเป็น POJO หรือไม่?

คำตอบ:


355

คุณสามารถบรรลุเป้าหมายนั้นด้วยแจ็คสันด้วยเช่นกัน (และดูเหมือนว่าจะสะดวกสบายมากขึ้นตั้งแต่คุณกำลังพิจารณาใช้แจ็คสัน)

ใช้ObjectMapper's convertValueวิธีการ:

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

ไม่จำเป็นต้องแปลงเป็นสตริง JSON หรืออย่างอื่น การแปลงโดยตรงทำได้เร็วกว่ามาก


8
คุณต้องรวมไลบรารีนี้เพื่อใช้ ObjectMappercompile 'com.fasterxml.jackson.core:jackson-databind:2.7.3'
Shajeel Afzal

5
การใช้ convertValue เป็นคำตอบที่ถูกต้อง แต่อย่าสร้างอินสแตนซ์ ObjectMapper ทุกครั้ง มีราคาแพงในการสร้างและปลอดภัยสำหรับเธรดดังนั้นสร้างและแคชไว้ที่ใดที่หนึ่ง
Glade

1
คุณรู้วิธีการทำสิ่งที่ตรงกันข้าม - หรือวิธีการแปลงวัตถุเป็นแผนที่ <String, Object>?
anon58192932

2
@RaduSimionescu คุณคิดออกวิธีการแปลงวัตถุลึกด้วยแผนที่ / รายการซ้อนกันเป็นMap<String, Object>ตัวอย่าง?
anon58192932

@ anon58192932 ใช้งานได้ถ้าคุณทำตามคำตอบนี้ ฉันแค่จัดการกับวัตถุแปลก ๆ บางอย่างซึ่งเป็นแบบจำลองรายการเป็นแผนที่และเมื่อการทำให้เป็นอันดับคือผลลัพธ์ที่ไม่คาดคิด แต่นั่นเป็นปัญหาอื่นไม่มีอะไรเกี่ยวข้องกับแจ็คสัน
Radu Simionescu

60

วิธีแก้ปัญหาด้วยGson :

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

1
สิ่งที่จะเป็นในทางกลับกัน
Prabs

2
@Prabs - ในทางกลับกันจะเป็นgson.toJson ()
AlikElzin-kilaka

ไม่จำเป็นต้องแปลงแผนที่เป็น json map.toString () เพียงพอแล้ว Gson gson = new Gson (); MyPojo pojo = gson.fromJson (map.toString (), MyPojo.class);
Esakkiappan.

1
@ Esakkiappan.E ทำไมคุณถึงคิดว่าmap.toString()จะให้สตริงที่ถูกต้อง? การใช้งานของtoString()ไม่รับประกันรูปแบบที่เฉพาะเจาะจง
AlikElzin-kilaka

4

ใช่เป็นไปได้แน่นอนที่จะหลีกเลี่ยงการแปลงระดับกลางเป็น JSON การใช้เครื่องมือทำสำเนาลึกเช่นDozerคุณสามารถแปลงแผนที่เป็น POJO ได้โดยตรง นี่คือตัวอย่างง่ายๆ

ตัวอย่าง 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());
    }
}

รหัสการแปลงตัวอย่าง:

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

เอาท์พุท:

MyPojo [id = 5, name = Bob, age = 23, saving = 2500.39]

หมายเหตุ: หากคุณเปลี่ยนซอร์สแผนที่เป็น a Map<String, Object>คุณสามารถคัดลอกคุณสมบัติที่ซ้อนกันแบบลึก ๆ ได้ (โดยMap<String, String>คุณจะได้รับเพียงหนึ่งระดับ)


1
คุณจะทำ "สำเนาลึก" จากแผนที่ไปยัง POJO ได้อย่างไร ตัวอย่างเช่นคุณมี User.class ซึ่ง encapsulate Address.class และแผนที่มีคีย์เช่น "address.city", "address.zip" และสิ่งเหล่านี้จำเป็นต้องแมปกับ User.Address.City และ User.Address.Zip ? ดูเหมือนจะไม่ตีความจุดในคีย์แผนที่โดยอัตโนมัติเป็นระดับย่อยไปยังกราฟวัตถุ
szxnyc

4

ถ้าคุณมีประเภททั่วไปในชั้นเรียนของคุณคุณควรใช้กับTypeReferenceconvertValue()

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

นอกจากนี้คุณสามารถใช้มันเพื่อแปลง pojo เป็นjava.util.Mapback

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

2

ฉันได้ทำการทดสอบทั้ง Jackson และ BeanUtils และพบว่า BeanUtils นั้นเร็วกว่ามาก
ในเครื่องของฉัน (Windows8.1, JDK1.7) ฉันได้รับผลลัพธ์นี้

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
ความแตกต่างคือ: แจ็คสันมีกรอบการแปลงประเภททั้งหมดด้วย เช่นการMapบรรจุmap.put("data","2016-06-26")และTestClassมีฟิลด์private LocalDate data;จากนั้นแจ็คสันก็สามารถทำสิ่งต่าง ๆ ได้ในขณะที่ BeanUtils จะล้มเหลว
Benjamin M

6
ฉันได้ยินมาว่าการสร้างObjectMapperอินสแตนซ์เป็นกระบวนการที่ใช้เวลา / ทรัพยากรและขอแนะนำให้ใช้อินสแตนซ์ mapper หนึ่งครั้งแทนที่จะสร้างใหม่ในแต่ละครั้ง ฉันคิดว่ามันจะเป็นการดีกว่าถ้าเอามันออกจากการทดสอบลพบุรี
Mixaz

3
ไม่ใช่การทดสอบที่ยุติธรรมเนื่องจาก BeanUtils สามารถแคชหลังจากการวนซ้ำครั้งแรกในขณะที่ ObjectMapper ไม่เคยได้รับโอกาส
ลูคัสรอส

1

คำตอบที่ให้ไว้เมื่อใช้แจ็คสันนั้นดีมาก แต่คุณก็ยังมีฟังก์ชั่นutilเพื่อช่วยคุณแปลงไฟล์ต่างPOJOๆ ดังนี้

    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

แปลงแผนที่เป็นตัวอย่าง POJO แจ้งให้ทราบถึงคีย์แผนที่ประกอบด้วยขีดเส้นใต้และตัวแปรฟิลด์คือโคก

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

App.class ทดสอบตัวอย่าง

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 หากใช้ข้อมูลจำนวนมากให้ใช้ Jackson เพื่อแปลงข้อมูลแสงใช้ 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; } }
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.