将 Map<String, String> 转换为 POJO

Posted

技术标签:

【中文标题】将 Map<String, String> 转换为 POJO【英文标题】:Convert a Map<String, String> to a POJO 【发布时间】:2013-05-01 23:36:15 【问题描述】:

我一直在查看 Jackson,但似乎我必须将 Map 转换为 JSON,然后将生成的 JSON 转换为 POJO。

有没有办法将 Map 直接转换为 POJO?

【问题讨论】:

【参考方案1】:

嗯,您也可以通过 Jackson 实现这一目标。 (因为您考虑使用杰克逊,它似乎更舒服)。

使用ObjectMapperconvertValue方法:

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

无需转换成JSON字符串或其他;直接转换的速度要快得多。

【讨论】:

你需要包含这个库才能使用 ObjectMapper compile 'com.fasterxml.jackson.core:jackson-databind:2.7.3' 使用 convertValue 是正确的答案,但不要每次都创建 ObjectMapper 实例。创建和线程安全的成本很高,因此创建一个并将其缓存在某处。 你知道如何做相反的事情 - 或者如何将对象转换为 Map @RaduSimionescu 你知道如何将带有嵌套地图/列表的对象深度转换为Map&lt;String, Object&gt; 实例吗? @anon58192932 如果您遵循此答案,它将起作用。我只是在处理一些奇怪的对象,这些对象将列表建模为地图,并且当序列化得到意想不到的结果时。但那是另一个问题,与杰克逊无关【参考方案2】:

Gson的解决方案:

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

【讨论】:

反之亦然 @Prabs - 反之亦然 gson.toJson() 无需将map转json。 map.toString() 就足够了。 Gson gson = 新 Gson(); MyPojo pojo = gson.fromJson(map.toString(), MyPojo.class); @Esakkiappan.E,您为什么认为map.toString() 会提供正确的字符串? toString() 的实现不保证特定格式。【参考方案3】:

如果你的类中有泛型类型,你应该使用TypeReferenceconvertValue()

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

您也可以使用它来将 pojo 转换为 java.util.Map 返回。

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

【讨论】:

使用 convertValue 将 Map 映射到 pojo 时,如何处理 Map 包含 dto 中不存在的字段的情况,如果字段相同,它可以工作,但是如果地图中的字段比 dto 多一个,那么它会抛出 IllegalArgumentException,如何处理这种情况,有什么想法或线索? @GurkiratSinghGuliani 你试过@JsonIgnoreProperties(ignoreUnknown = true) 吗? 嘿,用ObjectMapper objMapper = new ObjectMapper().configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); 搞定了【参考方案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,姓名 = Bob,年龄 = 23,储蓄 = 2500.39]

注意:如果您将源映射更改为Map&lt;String, Object&gt;,那么您可以复制任意深度的嵌套属性(使用Map&lt;String, String&gt;,您只能获得一层)。

【讨论】:

如何从 Map 到 POJO 进行“深拷贝”?比如说你有一个 User.class 封装了一个 Address.class 并且地图有一个像“address.city”,“address.zip”这样的键,这些需要映射到 User.Address.City 和 User.Address.Zip ?它似乎不会自动将 Map 键中的点解释为对象图的子级别。【参考方案5】:
 ObjectMapper objectMapper = new ObjectMapper();
 //if all properties are not in class use this
 objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
 final MyPojo pojo =     objectMapper.convertValue(map, MyPojo.class);

与第一个答案相同,但我使用它时出错,因为我不希望地图的所有属性都转换为 calss。我找到 objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);这是解决方案

【讨论】:

【参考方案6】:

我测试了 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));

【讨论】:

不同的是:Jackson 有一个完整的类型转换框架。例如Map 包含 map.put("data","2016-06-26")TestClass 有一个字段 private LocalDate data;,那么 Jackson 将能够完成任务,而 BeanUtils 将失败。 听说创建ObjectMapper 实例是一个耗费时间/资源的过程,建议重复使用一个映射器实例,而不是每次都重新创建它。我认为最好把它从测试 lop 中取出来 不是一个公平的测试,因为 BeanUtils 能够在第一次迭代后缓存,而 ObjectMapper 从来没有机会。【参考方案7】:

到目前为止使用 Jackson 提供的答案非常好,但您仍然可以使用 util 函数来帮助您转换不同的POJOs,如下所示:

    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;
    

【讨论】:

我知道这是一个离题的评论,但我认为忽略异常是个坏主意。因此,除了objectMapper.convertValue,我看不到这个实用函数的任何价值。【参考方案8】:

将 Map 转换为 POJO 示例。注意 Map 键包含下划线,字段变量是驼峰。

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)
 */

【讨论】:

【参考方案9】:

@Hamedz 如果使用大量数据,请使用 Jackson 转换 轻数据,使用 apache... 测试用例:

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;

【讨论】:

以上是关于将 Map<String, String> 转换为 POJO的主要内容,如果未能解决你的问题,请参考以下文章

如何将 XML 转换为 java.util.Map,反之亦然?

将 Map<String,Object> 转换为 Map<String,String>

将 Map<String,String> 转换为 Map<String,Object>

java将数据从List转换Map

Java 8:将 Map<String, List<String>> 转换为 Map<String, String[]> [重复]

使用Map飞镖HTTP POST 作为身体