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
A quick look at how to convert a POJO from/to a Map<K, V> with Jackson: // Create ObjectMapper instance ObjectMapper mapper = new ObjectMapper(); // Converting POJO to Map Map<String, Object> map = mapper. convertValue(foo, new TypeReference<Map<String, Object>>() {}); // Convert Map to POJO Foo anotherFoo = mapper.
Use Object#toString() . String string = map. toString();
One solution is to use reflection and reflectively read the fields of the object and create the map from it. The other approach is to create a toMap() method in the class that needs to be converted to a Map that simply adds each field to the returned map using the name of the field.
In Java 8, we have the ability to convert an object to another type using a map() method of Stream object with a lambda expression. The map() method is an intermediate operation in a stream object, so we need a terminal method to complete the stream.
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<>();
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;
}
If java.beans
API were available, then you could just do:
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;
}
Here's my own implementation without any dependencies. Rather than make a copy of the object's state, it implements a live Map over the pojo. Android doesn't support java.beans, but you can use openbeans instead.
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);
}
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With