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")
(whereattr1
andattr3
are a subset ofobj
'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