从POJO生成Map<String,String>

8
我有一个POJO,以及一个(目前尚未构建的)类,该类将返回POJO的列表。我希望自动生成访问POJO作为Map所需的代码。这是个好主意吗?是否可以自动完成,并且我需要为想要以此方式处理的每个POJO手动执行此操作?
谢谢, Andy
2个回答

16

你可以使用Commons BeanUtilsBeanMap来实现这个功能。

Map map = new BeanMap(someBean);

更新:由于Android中存在某些库依赖问题,所以这不是一种选项,因此这里提供一个基本示例,介绍如何使用反射API进行操作:

public static Map<String, Object> mapProperties(Object bean) throws Exception {
    Map<String, Object> properties = new HashMap<>();
    for (Method method : bean.getClass().getDeclaredMethods()) {
        if (Modifier.isPublic(method.getModifiers())
            && method.getParameterTypes().length == 0
            && method.getReturnType() != void.class
            && method.getName().matches("^(get|is).+")
        ) {
            String name = method.getName().replaceAll("^(get|is)", "");
            name = Character.toLowerCase(name.charAt(0)) + (name.length() > 1 ? name.substring(1) : "");
            Object value = method.invoke(bean);
            properties.put(name, value);
        }
    }
    return properties;
}
如果能够使用java.beans API,那么你只需要执行以下操作:
public static Map<String, Object> mapProperties(Object bean) throws Exception {
    Map<String, Object> properties = new HashMap<>();
    for (PropertyDescriptor property : Introspector.getBeanInfo(bean.getClass()).getPropertyDescriptors()) {
        String name = property.getName();
        Object value = property.getReadMethod().invoke(bean);
        properties.put(name, value);
    }
    return properties;
}

1
反射始终具有性能成本。无法避免。但是您可以相信BeanUtils团队已经尽可能地对其进行了优化。这是一个非常受欢迎的库。 - BalusC
很好。我需要在我的POJO上实现任何接口吗,还是只使用get*命名就足够了(我正在使用带有私有构造函数的不可变POJO,通过使用Builder进行实例化)? - Andrew Toulouse
那么,您可以借助反射编写一个实用方法。我已经添加了一个启动示例。 - BalusC
注意: method.getName().matches("^(get|is).+") 和 method.getName().replaceAll("^(get|is)", ""); 会使上述代码变慢...去掉正则表达式并使用 startsWith 会有很大的区别,当然对于几百个调用来说。 - jack_carver
@jack:没错,这只是一个基本的启动示例,没有太多的样板文件。请注意,它也不能正确处理以2个或更多大写字母开头的属性名。感谢您的建议。 - BalusC
显示剩余4条评论

1
这是我的独立实现,没有任何依赖关系。 它不是复制对象状态,而是在POJO上实现一个实时映射。 Android不支持java.beans,但您可以使用openbeans代替。
import java.beans.*;  // Or, import com.googlecode.openbeans.*
import java.util.*;

public class BeanMap extends AbstractMap<String, Object> {
    private static final Object[] NO_ARGS = new Object[] {};
    private HashMap<String, PropertyDescriptor> properties;
    private Object bean;

    public BeanMap(Object bean) throws IntrospectionException {
        this.bean = bean;
        properties = new HashMap<String, PropertyDescriptor>();
        BeanInfo info = Introspector.getBeanInfo(bean.getClass());
        for(PropertyDescriptor property : info.getPropertyDescriptors()) {
            properties.put(property.getName(), property);
        }
    }

    @Override public Object get(Object key) {
        PropertyDescriptor property = properties.get(key);
        if(property == null)
            return null;
        try {
            return property.getReadMethod().invoke(bean, NO_ARGS);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    @Override public Object put(String key, Object value) {
        PropertyDescriptor property = properties.get(key);
        try {
            return property.getWriteMethod().invoke(bean, new Object[] {value});
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    @Override public Set<Map.Entry<String, Object>> entrySet() {
        HashSet<Map.Entry<String, Object>> result = new HashSet<Map.Entry<String, Object>>(properties.size() * 2);
        for(PropertyDescriptor property : properties.values()) {
            String key = property.getName();
            Object value;
            try {
                value = property.getReadMethod().invoke(bean, NO_ARGS);
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
            result.add(new PropertyEntry(key, value));
        }
        return Collections.unmodifiableSet(result);
    }

    @Override public int size() { return properties.size(); }

    @Override public boolean containsKey(Object key) { 
        return properties.containsKey(key);
    }

    class PropertyEntry extends AbstractMap.SimpleEntry<String, Object> {
        PropertyEntry(String key, Object value) {
            super(key, value);
        }

        @Override public Object setValue(Object value) {
            super.setValue(value);
            return BeanMap.this.put(getKey(), value);
        }
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接