将Java Bean展平为地图

我坚持将Java Bean转换为Map 。 互联网上有很多资源,但不幸的是,它们都将简单的bean转换为地图。 我的那些更广泛。

有简化的例子:

 public class MyBean { private String firstName; private String lastName; private MyHomeAddress homeAddress; private int age; // getters & setters } 

我的观点是生成Map ,在这种情况下,对于以下条件是正确的:

 map.containsKey("firstName") map.containsKey("lastName") map.containsKey("homeAddress.street") // street is String map.containsKey("homeAddress.number") // number is int map.containsKey("homeAddress.city") // city is String map.containsKey("homeAddress.zipcode") // zipcode is String map.containsKey("age") 

我尝试过使用Apache Commons BeanUtils 。 两种方法BeanUtils#describe(Object) BeanMap(Object) BeanUtils#describe(Object)BeanMap(Object)生成一个“深层次”为1的Map(我的意思是只有"homeAddress"键,将MyHomeAddress对象保存为值)。 我的方法应该越来越深入地进入对象,直到它遇到基本类型(或字符串),然后它应该停止挖掘和插入键,即"order.customer.contactInfo.home"

所以,我的问题是:如何轻松完成(或者是否已经存在允许我这样做的项目)?

更新

我已经扩展了Radiodef的答案,还包括Collections,Maps Arrays和Enums:

 private static boolean isValue(Object value) { final Class clazz = value.getClass(); if (value == null || valueClasses.contains(clazz) || Collection.class.isAssignableFrom(clazz) || Map.class.isAssignableFrom(clazz) || value.getClass().isArray() || value.getClass().isEnum()) { return true; } return false; } 

这是一个简单的reflection/递归示例。

您应该知道,按照您提出的方式进行转换存在一些问题:

  • 地图键必须是唯一的。
  • Java允许类将其私有字段命名为与inheritance类拥有的私有字段相同的名称。

这个例子没有解决这些问题,因为我不确定你想如何解释它们(如果你这样做)。 如果你的beaninheritance自Object以外的东西,你需要稍微改变一下你的想法。 此示例仅考虑子类的字段。

换句话说,如果你有

 public class SubBean extends Bean { 

此示例仅返回SubBean字段。

Java让我们做这个疯狂的事情:

 package com.company.util; public class Bean { private int value; } package com.company.misc; public class Bean extends com.company.util.Bean { private int value; } 

并不是说任何人都应该这样做,但如果你想使用String作为键,这就是一个问题。

这是代码:

 import java.lang.reflect.*; import java.util.*; public final class BeanFlattener { private BeanFlattener() {} public static Map deepToMap(Object bean) throws IllegalAccessException { Map map = new LinkedHashMap<>(); putValues(bean, map, null); return map; } private static void putValues( Object bean, Map map, String prefix ) throws IllegalAccessException { Class cls = bean.getClass(); for(Field field : cls.getDeclaredFields()) { field.setAccessible(true); Object value = field.get(bean); String key; if(prefix == null) { key = field.getName(); } else { key = prefix + "." + field.getName(); } if(isValue(value)) { map.put(key, value); } else { putValues(value, map, key); } } } private static final Set> valueClasses = ( Collections.unmodifiableSet(new HashSet<>(Arrays.asList( Object.class, String.class, Boolean.class, Character.class, Byte.class, Short.class, Integer.class, Long.class, Float.class, Double.class ))) ); private static boolean isValue(Object value) { return value == null || valueClasses.contains(value.getClass()); } } 

你总是可以使用Jackson Json处理器 。 喜欢这个:

 import com.fasterxml.jackson.databind.ObjectMapper; //... ObjectMapper objectMapper = new ObjectMapper(); //... @SuppressWarnings("unchecked") Map map = objectMapper.convertValue(pojo, Map.class); 

其中pojo是一些Java bean。 您可以在bean上使用一些不错的注释来控制序列化。

您可以重用ObjectMapper。