我坚持将
Java Bean转换为Map.互联网上有很多资源,但不幸的是,它们都将简单的bean转换为地图.我的那些更广泛.
有简化的例子:
public class MyBean { private String firstName; private String lastName; private MyHomeAddress homeAddress; private int age; // getters & setters }
我的观点是生成Map< String,Object>在这种情况下,对于以下条件是正确的:
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 #thedes(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; }
解决方法
这是一个简单的反射/递归示例.
您应该知道,按照您提出的方式进行转换存在一些问题:
>地图键必须是唯一的.
> Java允许类将其私有字段命名为与继承类拥有的私有字段相同的名称.
这个例子没有解决这些问题,因为我不确定你想如何解释它们(如果你这样做).如果你的bean继承自Object以外的东西,你需要稍微改变一下你的想法.此示例仅考虑子类的字段.
换句话说,如果你有
public class SubBean extends Bean {
此示例仅返回SubBean中的字段.
Java让我们这样做:
package com.acme.util; public class Bean { private int value; } package com.acme.misc; public class Bean extends com.acme.util.Bean { private int value; }
并不是说任何人都应该这样做,但是如果你想使用String作为键是一个问题,因为会有两个名为“value”的键.
import java.lang.reflect.*; import java.util.*; public final class BeanFlattener { private BeanFlattener() {} public static Map<String,Object> deepToMap(Object bean) { Map<String,Object> map = new LinkedHashMap<>(); try { putValues(bean,map,null); } catch (IllegalAccessException x) { throw new IllegalArgumentException(x); } return map; } private static void putValues(Object bean,Map<String,Object> map,String prefix) throws IllegalAccessException { Class<?> cls = bean.getClass(); for (Field field : cls.getDeclaredFields()) { if (field.isSynthetic() || Modifier.isStatic(field.getModifiers())) continue; 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,key); } } } private static final Set<Class<?>> VALUE_CLASSES = 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 // etc. ))); private static boolean isValue(Object value) { return value == null || value instanceof Enum<?> || VALUE_CLASSES.contains(value.getClass()); } }