Java中将对象转换为Map
本教程将教您如何使用Jackson和Gson API提供的不同方式将对象转换为Java Map。您还可以使用Java反射创建自己的解决方案,除非提供的解决方案不适用时,否则不建议重新造轮子。
1.介绍
在本教程中,我们将把以下Employee类的实例转换为Map。Employee类包含简单类型,如String,以及新的Java类型,如LocalDate和集合类型。
我们有一个Role类型的List,以进一步演示不同解决方案的行为。我们将看到不同的解决方案如何将转换后的Map中的嵌套类型进行转换。
class Employee { private Integer id; private String name; private LocalDate dateOfBirth; private List<String> locations; private List<Role> roles; } class Role { private Integer id; private String name; }
2.使用Jackson
Jackson是一个多用途的库,非常好地支持不同类型的转换,如JSON或XML。Jackson还支持使用以下方式将对象转换为Map:
2.1. 使用ObjectMapper.convertValue()
convertValue()方法从给定值转换为给定值类型的实例进行两步转换。它首先将给定值序列化为JSON,然后将JSON数据绑定到给定类型的值上。但由于不需要进行完全序列化,所以转换更加高效。
请注意,在以下示例中,我们正在注册JavaTimeModule类,因为Jackson默认不支持新的Java 8日期时间类。
Employee employee = new Employee(1, "Alex", LocalDate.of(1995, 1, 2), List.of("Delhi", "Nevada"), List.of(new Role(11, "Finance"), new Role(12, "HR"))); System.out.println(convertObjectToMapUsingObjectMapper(employee)); //转换方法 static Map<String, String> convertObjectToMapUsingObjectMapper(Employee employee) { ObjectMapper objectMapper = new ObjectMapper(); objectMapper.registerModule(new JavaTimeModule()); objectMapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd")); return objectMapper.convertValue(employee, Map.class); }
输出:
{ id=1, name=Alex, dateOfBirth=1995-01-02, locations=[Delhi, Nevada], roles=[ {id=11, name=Finance}, {id=12, name=HR} ] }
请注意,此方法还将关联和嵌套类(例如Role)转换为LinkedHashMap。
2.2. 使用JavaPropsMapper将其转换为Properties
另一种有趣的解决方案是将对象转换为Properties。Properties具有扁平结构。即使嵌套结构和集合也会转换为扁平结构,所有字段/值都会转换为字符串。在某些情况下,这可能是一个不错的解决方案。
Employee employee = new Employee(1, "Alex", LocalDate.of(1995, 1, 2), List.of("Delhi", "Nevada"), List.of(new Role(11, "Finance"), new Role(12, "HR"))); System.out.println(convertObjectToMapUsingJavaPropsMapper(employee)); //转换方法 static Properties convertObjectToMapUsingJavaPropsMapper(Employee employee) throws IOException { JavaPropsMapper mapper = new JavaPropsMapper(); mapper.registerModule(new JavaTimeModule()); mapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd")); Properties properties = mapper.writeValueAsProperties(employee); return properties; }
输出:
{ id=1, name=Alex, dateOfBirth=1995-01-02, locations.1=Delhi, locations.2=Nevada, roles.1.id=11, roles.1.name=Finance, roles.2.id=12, roles.2.name=HR }
3.使用Gson的TypeToken
如果我们的项目已经在应用程序中具有Gson依赖项,我们可以考虑使用Gson.fromJson()将对象转换为JSON,然后在第二步中将JSON转换为HashMap。
这种技术使用完全的序列化和反序列化,因此性能可能不如Jackson。如果由于某种原因无法使用Jackson,那么可以考虑使用Gson。
Employee employee = new Employee(1, "Alex", LocalDate.of(1995, 1, 2), List.of("Delhi", "Nevada"), List.of(new Role(11, "Finance"), new Role(12, "HR"))); System.out.println(convertObjectToMapUsingGson(employee)); static Map<String, String> convertObjectToMapUsingGson(Employee employee) { Gson gson = new GsonBuilder() .registerTypeAdapter(LocalDate.class, new LocalDateAdapter()) .create(); return gson.fromJson(gson.toJson(employee), new TypeToken<HashMap<String, Object>>() { }.getType() ); }
程序输出:
{ id=1.0, name=Alex, dateOfBirth=1995-01-02, locations=[Delhi, Nevada], roles=[ {id=11.0, name=Finance}, {id=12.0, name=HR} ] }
Gson和Jackson之间主要有两个差异:
- Gson默认将所有数值转换为Double类型。
- Gson在嵌套类中使用LinkedTreeMap,而Jackson使用LinkedHashMap。
4.使用反射
另一种可能的方法,尽管不建议使用,是使用反射。使用反射,我们必须自己编写所有逻辑,因此存在出错的机会。如果采用这种方法,请确保在部署到生产环境之前进行充分测试。
以下是一个用于简单POJO类的非常基本的代码片段,它依赖于按名称获取字段的值。如果有这样的要求,我们可以调整方法以通过其getter获取值。
public static Map<String, Object> toKeyValuePairs(Object instance) { return Arrays.stream(Employee.class.getDeclaredFields()) .collect(Collectors.toMap( Field::getName, field -> { try { Object result = null; field.setAccessible(true); result = field.get(instance); return result != null ? result : ""; } catch (Exception e) { return ""; } })); }
程序输出:
{ id=1, name=Alex, dateOfBirth=1995-01-02, locations=[Delhi, Nevada], roles=[ Role(id=11, name=Finance), Role(id=12, name=HR) ] }
我们可以发现Map结构保持了嵌套类的原样,因为我们没有访问它们或在我们的逻辑中处理它们。如果需要在应用程序中处理它们,那么请进一步扩展toKeyValuePairs()方法中的逻辑。
5.结论
这个Java教程教会了我们如何使用不同的解决方案将给定的Java对象转换为Map。通过查看所有解决方案,从易用性和性能的角度来看,Jackson似乎是最佳解决方案,并在大多数情况下是推荐的方法。
对于创建扁平结构,我们可以使用JavaPropsMapper将对象转换为Properties。
使用Gson与Jackson非常相似,但它没有提供任何额外的优势,而所有数字都被转换为Double,这在某些情况下可能不是一个理想的情况。因此,当无法使用Jackson时使用Gson。
最后,反射可以完全掌握在您手中,您必须自己编写每种可能的用例/条件。