2010-07-09 13 views
8

Tengo un POJO y una clase (actualmente aún no construida) que devolverá las Listas. Me gustaría generar automáticamente el código necesario para acceder al POJO como un mapa. ¿Es esta una buena idea? ¿Es posible hacerlo automáticamente? ¿Debo hacerlo manualmente para cada POJO que quiera tratar de esta manera?Generar mapa <String, String> de POJO

Gracias, Andy

Respuesta

16

Usted puede utilizar Commons BeanUtilsBeanMap para esto.

Map map = new BeanMap(someBean); 

actualización: puesto que no es una opción debido a algunos problemas de dependencias biblioteca aparentes en Android, aquí hay un ejemplo básico patada de cómo podría hacerlo con poca ayuda de 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; 
} 

Si java.beans API estaban disponibles, entonces podría hacer:

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; 
} 
+0

Holy crap! ¡Eso parece súper útil! ¿Sabes algo sobre sus implicaciones de rendimiento? –

+1

La reflexión siempre tiene un costo de rendimiento. Nada contra lo que hacer. Pero puedes confiar en que los chicos de BeanUtils lo han optimizado tanto como sea posible. Es una biblioteca bastante popular. – BalusC

+0

Cool. ¿Tengo que implementar algún tipo de interfaz en mis POJO, o estoy usando get * naming enough (estoy usando POJOs inmutables con constructores privados instanciados usando un Builder)? –

1

Aquí está mi propia implementación sin dependencias. En lugar de hacer una copia del estado del objeto, implementa un Mapa en vivo sobre el pojo. Android doesn't support java.beans, pero puede usar openbeans en su lugar.

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); 
     } 
    } 
} 
Cuestiones relacionadas