views:

132

answers:

1

I have a POJO, and a (currently not-yet-built) class that will return Lists of it. I'd like to automatically generate the code necessary for the POJO to be accessed as a Map. Is this a good idea, is it possible to do automatically, and do I need to do this manually for every POJO I want to treat this way?

Thanks, Andy

+3  A: 

You can use Commons BeanUtils BeanMap for this.

Map map = new BeanMap(someBean);

Update: since that's not an option due to some apparent library dependency problems in Android, here's a basic kickoff example how you could do it with little help of Reflection API:

public static Map<String, Object> mapProperties(Object bean) throws Exception {
    Map<String, Object> properties = new HashMap<String, Object>();
    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;
}

I am not sure if java.beans API is available in Android, but if true, then you could replace

name = Character.toLowerCase(name.charAt(0)) + (name.length() > 1 ? name.substring(1) : "");

by

name = Introspector.decapitalize(name);
BalusC
Holy crap! That looks super useful! Do you know anything about its performance implications?
Andrew Toulouse
Reflection has always a performance cost. Nothing to do against. But you can trust that the BeanUtils guys have optimized it as much as possible. It's a pretty popular library.
BalusC
Cool. Do I have to implement any sort of interface on my POJOs, or is using get* naming enough (I'm using immutable POJOs w/ private constructors instantiated by using a Builder)?
Andrew Toulouse
It just follows the [JavaBean™ specification](http://java.sun.com/javase/technologies/desktop/javabeans/docs/spec.html), yes. Not sure about private c'tors though, but a public no-arg constructor does logically not seem to be necessary to generate a map based on an existing instance.
BalusC
I agree. \*crosses fingers\*. Thanks for your help!
Andrew Toulouse
Hm. Android doesn't seem to like Commons BeanUtils. Importing BeanUtils causes a runtime error resolving org.apache.commons.collections.Transformer. Adding in Commons collections further causes issues because it can't be packaged into an apk for whatever reason. :(
Andrew Toulouse
Well, then write an utility method yourself with a little help of reflection. I've added a kickoff example.
BalusC