Recursive BeanUtils.describe()

后端 未结 3 1685
南笙
南笙 2020-12-31 06:45

Is there a version of BeanUtils.describe(customer) that recursively calls the describe() method on the complex attributes of \'customer\'.

class Customer {

         


        
3条回答
  •  孤城傲影
    2020-12-31 07:22

    Funny, I would like the describe method to retrieve the contents of nested attributes as well, I don't understand why it doesn't. I went ahead and rolled my own, though. Here it is, you can just call:

    Map beanMap = BeanUtils.recursiveDescribe(customer); 
    

    A couple of caveats.

    1. I'm wasn't sure how commons BeanUtils formatted attributes in collections, so i went with "attribute[index]".
    2. I'm wasn't sure how it formatted attributes in maps, so i went with "attribute[key]".
    3. For name collisions the precedence is this: First properties are loaded from the fields of super classes, then the class, then from the getter methods.
    4. I haven't analyzed the performance of this method. If you have objects with large collections of objects that also contain collections, you might have some issues.
    5. This is alpha code, not garunteed to be bug free.
    6. I am assuming that you have the latest version of commons beanutils

    Also, fyi, this is roughly taken from a project I've been working on called, affectionately, java in jails so you could just download it and then run:

    Map beanMap = new SimpleMapper().toMap(customer);
    

    Though, you'll notice that it returns a String[], instead of a String, which may not work for your needs. Anyway, the below code should work, so have at it!

    public class BeanUtils {
        public static Map recursiveDescribe(Object object) {
            Set cache = new HashSet();
            return recursiveDescribe(object, null, cache);
        }
    
        private static Map 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 beanMap = new TreeMap();
    
            Map 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 getProperties(Object object) {
            Map propertyMap = getFields(object);
            //getters take precedence in case of any name collisions
            propertyMap.putAll(getGetterMethods(object));
            return propertyMap;
        }
    
        private static Map getGetterMethods(Object object) {
            Map result = new HashMap();
            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 getFields(Object object) {
            return getFields(object, object.getClass());
        }
    
        private static Map getFields(Object object, Class classType) {
            Map result = new HashMap();
    
            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 convertAll(Collection values, String key, Set cache) {
            Map valuesMap = new HashMap();
            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 convertMap(Map values, String key, Set cache) {
            Map valuesMap = new HashMap();
            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 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 valueMap = new HashMap();
                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);
            }
        }
    }
    
        

    提交回复
    热议问题