首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >递归BeanUtils.describe()

递归BeanUtils.describe()
EN

Stack Overflow用户
提问于 2011-05-26 12:26:08
回答 3查看 19.3K关注 0票数 14

是否有一个版本的BeanUtils.describe(customer)可以递归地调用'customer‘的复杂属性上的describe()方法。

代码语言:javascript
复制
class Customer {

String id;
Address address;

}

在这里,我希望describe方法也能检索address属性的内容。

目前,我能看到的类名如下:

代码语言:javascript
复制
{id=123, address=com.test.entities.Address@2a340e}
EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2011-12-08 03:50:44

有趣的是,我希望describe方法也能检索嵌套属性的内容,我不明白为什么它不能。在这里,您只需调用:

代码语言:javascript
复制
Map<String,String> beanMap = BeanUtils.recursiveDescribe(customer); 

有几个注意事项。

  1. 我不确定commons BeanUtils如何格式化集合中的属性,所以我选择了"attributeindex“。
  2. 我不确定它是如何在地图中格式化属性的,所以我使用了"attributekey“。
  3. 对于名称冲突,优先级是这样的:首先从超类的字段加载属性,然后是类,然后是getter方法。
  4. 我还没有分析过这个方法的性能。如果您的对象包含包含集合的大型对象集合,则可能会遇到一些问题。
  5. 这是alpha代码,并不保证没有错误。
  6. 我假设您有最新版本的commons beanutils

另外,仅供参考,这是我一直在做的一个项目的大致内容,这个项目名为java in jails,所以你可以下载它,然后运行:

代码语言:javascript
复制
Map<String, String[]> beanMap = new SimpleMapper().toMap(customer);

不过,您会注意到它返回的是一个String[],而不是字符串,这可能无法满足您的需要。无论如何,下面的代码应该可以工作,所以一定要尝试一下!

代码语言:javascript
复制
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);
        }
    }
}
票数 11
EN

Stack Overflow用户

发布于 2011-05-26 12:32:49

挑战(或显示停止程序)是我们必须处理对象图而不是简单的树的问题。图可能包含循环,这需要为递归算法中的停止条件开发一些自定义规则或要求。

看一看一个简单的bean (一个树形结构,假设有getter,但没有显示):

代码语言:javascript
复制
public class Node {
   private Node parent;
   private Node left;
   private Node right;
}

并像这样初始化它:

代码语言:javascript
复制
        root
        /  \
       A    B

现在在root上调用describe。非递归调用将导致

代码语言:javascript
复制
{parent=null, left=A, right=B}

相反,递归调用将执行

代码语言:javascript
复制
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)}}

并遇到一个StackOverflowError,因为describe被反复调用对象root、A和B。

自定义实现的一种解决方案可能是记住到目前为止描述过的所有对象(将这些实例记录在一个集合中,如果set.contains(bean)返回true则停止),并在结果对象中存储某种类型的链接。

票数 7
EN

Stack Overflow用户

发布于 2014-07-16 08:42:18

您可以从相同的commom beanutils中轻松使用:

代码语言:javascript
复制
Map<String, Object> result = PropertyUtils.describe(obj);

返回指定bean为其提供读取方法的整个属性集。

票数 6
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/6133660

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档