2010-07-09 10 views
8

Ho una classe POJO e una (attualmente non ancora generata) che restituirà elenchi di essa. Mi piacerebbe generare automaticamente il codice necessario affinché il POJO sia accessibile come una mappa. È una buona idea, è possibile farlo automaticamente, e devo farlo manualmente per ogni POJO che voglio trattare in questo modo?Genera mappa <String, String> da POJO

Grazie, Andy

risposta

16

È possibile utilizzare Commons BeanUtilsBeanMap per questo.

Map map = new BeanMap(someBean); 

Aggiornamento: dal momento che non è un'opzione a causa di alcuni problemi di biblioteca dipendenza apparenti in Android, ecco un esempio di base kickoff come si potrebbe fare con piccolo aiuto di 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; 
} 

Se java.beans API fosse disponibile, allora si può solo fare:

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

Santo schifo! Sembra super utile! Sai qualcosa delle sue implicazioni in termini di prestazioni? –

+1

Reflection ha sempre un costo in termini di prestazioni. Niente da fare contro. Ma puoi fidarti che i ragazzi di BeanUtils l'hanno ottimizzato il più possibile. È una libreria molto popolare. – BalusC

+0

Freddo. Devo implementare qualsiasi tipo di interfaccia sui miei POJO, o sto usando get * naming abbastanza (sto usando POI immutabili con costruttori privati ​​istanziati usando un Builder)? –

1

Ecco la mia implementazione senza alcuna dipendenza. Piuttosto che fare una copia dello stato dell'oggetto, implementa una mappa live sul pojo. Android doesn't support java.beans, ma è possibile utilizzare invece openbeans.

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); 
     } 
    } 
} 
Problemi correlati