将Map<String, String>转换为POJO类

306

我一直在研究Jackson,但似乎需要先将Map转换为JSON,然后再将生成的JSON转换为POJO。

有没有一种直接将Map转换为POJO的方法?

9个回答

557

嗯,你也可以用Jackson来实现这一点(似乎更方便,因为你正在考虑使用jackson)。

使用ObjectMapperconvertValue方法:

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

不需要将其转换为JSON字符串或其他格式;直接转换速度更快。


12
你需要包含这个库才能使用ObjectMapper:compile 'com.fasterxml.jackson.core:jackson-databind:2.7.3' - Shajeel Afzal
12
使用convertValue是正确的答案,但不要每次都创建一个ObjectMapper实例。创建它很昂贵并且线程安全,因此创建一个并将其缓存到某个地方即可。 - glade
1
你知道如何做相反的操作吗?或者如何将一个对象转换为Map<String, Object>? - fIwJlxSzApHEZIl
3
@RaduSimionescu,你找出如何将具有嵌套映射/列表的对象深度转换为Map<String, Object>实例了吗? - fIwJlxSzApHEZIl
1
数据类型怎么样?它能处理 Long 转 int 吗(例如)? - markthegrea
显示剩余2条评论

85

使用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
无需将map转换为json。使用map.toString()即可。Gson gson = new Gson(); MyPojo pojo = gson.fromJson(map.toString(), MyPojo.class); - Esakkiappan .E
3
为什么你认为 map.toString() 会提供正确的字符串?toString() 的实现并不保证特定格式。 - AlikElzin-kilaka

15
ObjectMapper objectMapper = new ObjectMapper();
// Use this if all properties are not in the class
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
final MyPojo pojo = objectMapper.convertValue(map, MyPojo.class);

和第一个答案一样,但我使用它时出了一个错误,因为我不想将Map的所有属性转换为类。我还发现objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);是解决方案。


10

如果你的类中有泛型类型,你应该在使用convertValue()时使用TypeReference

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<String,Object>映射到POJO时,如何处理Map<String,Object>包含的不在DTO中的字段的情况? 如果字段相同,则可以正常工作,但是如果Map中有一个以上的字段而DTO中没有,则会抛出IllegalArgumentException异常。如何处理这种情况?有什么想法或线索吗? - Gurkirat Singh Guliani
@GurkiratSinghGuliani 你试过使用 @JsonIgnoreProperties(ignoreUnknown = true) 吗? - bhdrk
嘿,明白了,使用以下代码: new ObjectMapper().configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); - Gurkirat Singh Guliani

5

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

6
区别在于:Jackson带有完整的类型转换框架。例如,假设Map包含map.put("data","2016-06-26"),而TestClass具有一个字段private LocalDate data;,那么使用Jackson可以完成这个任务,但是BeanUtils则会失败。 - Benjamin M
8
我听说创建ObjectMapper实例需要很多时间和资源,因此建议重复使用一个映射器实例,而不是每次都创建新的实例。我认为最好将其从测试循环中移出。 - Mixaz
4
这并不是一个公平的测试,因为BeanUtils可以在第一次迭代后缓存结果,而ObjectMapper从未有机会这样做。 - Lucas Ross

5

是的,绝对可以避免中间转换为JSON。使用类似Dozer的深拷贝工具,您可以直接将map转换为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, savings = 2500.39]

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


1
你如何从Map复制到POJO进行“深度复制”?例如,你有一个User.class类,它封装了一个Address.class类,而且Map的键名为“address.city”,“address.zip”,需要映射到User.Address.City和User.Address.Zip。但似乎Map键中的点不会自动解释为对象图中的子级。 - szxnyc

3
到目前为止使用Jackson提供的答案很好,但是您仍然可以编写一个工具函数来帮助您将不同的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;
    }

2
我知道这是一个离题的评论,但我认为忽略异常是一个坏主意。因此,除了objectMapper.convertValue之外,我不认为这个实用函数有任何价值。 - Jongwook Choi

2

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

1

@Hamedz if use many data, use Jackson to convert light data, use apache... TestCase:


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接