Java introspection: object to map

I have a Java object obj that has attributes obj.attr1, obj.attr2 etc. The attributes are possibly accessed through an extra level of indirection: obj.getAttr1(), obj.getAttr2(), if not public.

The challenge: I want a function that takes an object, and returns a Map<String, Object>, where the keys are strings "attr1", "attr2" etc. and values are the corresponding objects obj.attr1, obj.attr2. I imagine the function would be invoked with something like

  • toMap(obj),
  • or toMap(obj, "attr1", "attr3") (where attr1 and attr3 are a subset of obj's attributes),
  • or perhaps toMap(obj, "getAttr1", "getAttr3") if necessary.

I don't know much about Java's introspection: how do you do that in Java?

Right now, I have a specialized toMap() implementation for each object type that I care about, and it's too much boilerplate.


NOTE: for those who know Python, I want something like obj.__dict__. Or dict((attr, obj.__getattribute__(attr)) for attr in attr_list) for the subset variant.


Solution 1:

Another way to user JacksonObjectMapper is the convertValue ex:

 ObjectMapper m = new ObjectMapper();
 Map<String,Object> mappedObject = m..convertValue(myObject, new TypeReference<Map<String, String>>() {});

Solution 2:

Use Apache Commons BeanUtils: http://commons.apache.org/beanutils/.

An implementation of Map for JavaBeans which uses introspection to get and put properties in the bean:

Map<Object, Object> introspected = new org.apache.commons.beanutils.BeanMap(object); 

Note: despite the fact the API returns Map<Object, Object> (since 1.9.0), the actual class for keys in the returned map is java.lang.String

Solution 3:

You can use JavaBeans introspection for this. Read up on the java.beans.Introspector class:

public static Map<String, Object> introspect(Object obj) throws Exception {
    Map<String, Object> result = new HashMap<String, Object>();
    BeanInfo info = Introspector.getBeanInfo(obj.getClass());
    for (PropertyDescriptor pd : info.getPropertyDescriptors()) {
        Method reader = pd.getReadMethod();
        if (reader != null)
            result.put(pd.getName(), reader.invoke(obj));
    }
    return result;
}

Big caveat: My code deals with getter methods only; it will not find naked fields. For fields, see highlycaffeinated's answer. :-) (You will probably want to combine the two approaches.)

Solution 4:

Here's a rough approximation, hopefully enough to get you pointed in the right direction:

public Map<String, Object> getMap(Object o) {
    Map<String, Object> result = new HashMap<String, Object>();
    Field[] declaredFields = o.getClass().getDeclaredFields();
    for (Field field : declaredFields) {
        result.put(field.getName(), field.get(o));
    }
    return result;
}

Solution 5:

Here is a really easy way to do this.

Use Jackson JSON lib to convert the object to JSON.

Then read the JSON and convert it to a Map.

The map will contain everything you want.

Here is the 4 liner

ObjectMapper om = new ObjectMapper();
StringWriter sw = new StringWriter();
om.writeValue(object, sw);
Map<String, Object> map = om.readValue(sw.toString(), Map.class);

And additional win of course is that this is recursive and will create maps of maps if it needs to