是否有一个版本的BeanUtils.describe(customer)可以递归地调用“customer”复杂属性上的 describe() 方法。
class Customer {
String id;
Address address;
}
我希望describe方法也能够获取address属性的内容。
目前,我只能看到类的名称如下:
{id=123, address=com.test.entities.Address@2a340e}
是否有一个版本的BeanUtils.describe(customer)可以递归地调用“customer”复杂属性上的 describe() 方法。
class Customer {
String id;
Address address;
}
我希望describe方法也能够获取address属性的内容。
目前,我只能看到类的名称如下:
{id=123, address=com.test.entities.Address@2a340e}
Map<String,String> beanMap = BeanUtils.recursiveDescribe(customer);
一些注意事项。
另外,FYI,这大致取自我正在开发的一个名为java in jails的项目,因此您可以下载它,然后运行:
Map<String, String[]> beanMap = new SimpleMapper().toMap(customer);
虽然你会注意到它返回的是一个String[],而不是一个String,这可能不适合你的需求。无论如何,下面的代码应该可以工作,所以试试吧!
public class BeanUtils {
public static Map<String, String> recursiveDescribe(Object object) {
Set cache = new HashSet();
return recursiveDescribe(object, null, cache);
}
private static Map<String, String> recursiveDescribe(Object object, String prefix, Set cache) {
if (object == null || cache.contains(object)) return Collections.EMPTY_MAP;
cache.add(object);
prefix = (prefix != null) ? prefix + "." : "";
Map<String, String> beanMap = new TreeMap<String, String>();
Map<String, Object> properties = getProperties(object);
for (String property : properties.keySet()) {
Object value = properties.get(property);
try {
if (value == null) {
//ignore nulls
} else if (Collection.class.isAssignableFrom(value.getClass())) {
beanMap.putAll(convertAll((Collection) value, prefix + property, cache));
} else if (value.getClass().isArray()) {
beanMap.putAll(convertAll(Arrays.asList((Object[]) value), prefix + property, cache));
} else if (Map.class.isAssignableFrom(value.getClass())) {
beanMap.putAll(convertMap((Map) value, prefix + property, cache));
} else {
beanMap.putAll(convertObject(value, prefix + property, cache));
}
} catch (Exception e) {
e.printStackTrace();
}
}
return beanMap;
}
private static Map<String, Object> getProperties(Object object) {
Map<String, Object> propertyMap = getFields(object);
//getters take precedence in case of any name collisions
propertyMap.putAll(getGetterMethods(object));
return propertyMap;
}
private static Map<String, Object> getGetterMethods(Object object) {
Map<String, Object> result = new HashMap<String, Object>();
BeanInfo info;
try {
info = Introspector.getBeanInfo(object.getClass());
for (PropertyDescriptor pd : info.getPropertyDescriptors()) {
Method reader = pd.getReadMethod();
if (reader != null) {
String name = pd.getName();
if (!"class".equals(name)) {
try {
Object value = reader.invoke(object);
result.put(name, value);
} catch (Exception e) {
//you can choose to do something here
}
}
}
}
} catch (IntrospectionException e) {
//you can choose to do something here
} finally {
return result;
}
}
private static Map<String, Object> getFields(Object object) {
return getFields(object, object.getClass());
}
private static Map<String, Object> getFields(Object object, Class<?> classType) {
Map<String, Object> result = new HashMap<String, Object>();
Class superClass = classType.getSuperclass();
if (superClass != null) result.putAll(getFields(object, superClass));
//get public fields only
Field[] fields = classType.getFields();
for (Field field : fields) {
try {
result.put(field.getName(), field.get(object));
} catch (IllegalAccessException e) {
//you can choose to do something here
}
}
return result;
}
private static Map<String, String> convertAll(Collection<Object> values, String key, Set cache) {
Map<String, String> valuesMap = new HashMap<String, String>();
Object[] valArray = values.toArray();
for (int i = 0; i < valArray.length; i++) {
Object value = valArray[i];
if (value != null) valuesMap.putAll(convertObject(value, key + "[" + i + "]", cache));
}
return valuesMap;
}
private static Map<String, String> convertMap(Map<Object, Object> values, String key, Set cache) {
Map<String, String> valuesMap = new HashMap<String, String>();
for (Object thisKey : values.keySet()) {
Object value = values.get(thisKey);
if (value != null) valuesMap.putAll(convertObject(value, key + "[" + thisKey + "]", cache));
}
return valuesMap;
}
private static ConvertUtilsBean converter = BeanUtilsBean.getInstance().getConvertUtils();
private static Map<String, String> convertObject(Object value, String key, Set cache) {
//if this type has a registered converted, then get the string and return
if (converter.lookup(value.getClass()) != null) {
String stringValue = converter.convert(value);
Map<String, String> valueMap = new HashMap<String, String>();
valueMap.put(key, stringValue);
return valueMap;
} else {
//otherwise, treat it as a nested bean that needs to be described itself
return recursiveDescribe(value, key, cache);
}
}
}
public class Node {
private Node parent;
private Node left;
private Node right;
}
并且可以像这样初始化它:
root
/ \
A B
root
调用describe。非递归调用将导致...{parent=null, left=A, right=B}
1: describe(root) =>
2: {parent=describe(null), left=describe(A), right=describe(B)} =>
3: {parent=null,
{A.parent=describe(root), A.left=describe(null), A.right= describe(null)}
{B.parent=describe(root), B.left=describe(null), B.right= describe(null)}}
如果反复使用对象root、A和B调用describe方法,可能会遇到StackOverflowError错误。
一个自定义实现的解决方案是记住所有已经描述过的对象(在集合中记录这些实例),如果set.contains(bean)返回true,则停止,并在结果对象中存储某种链接。
describe
返回一个字符串 - 这是不正确的,结果是一个Map
。 - Andreas Dolk您可以使用相同的 commom-beanutils 工具来简单地完成:
Map<String, Object> result = PropertyUtils.describe(obj);
返回指定bean提供读取方法的所有属性集。
path(key)
而不是path[key]
。因此,如果您回到这段代码,请使用相同的语法比较好。 - ARRGcache.contains(object)
语句。 - membersound