Recuperare programmaticamente i campi di un oggetto

DOMANDA:

Come posso recuperare tutti i campi di un oggetto programmaticamente?



RISPOSTA:

Per recuperare tutti gli attributi/campi di un oggetto è sufficiente usare la Reflection di Java con un po' di ricorsione. 

Definiamo una classe User con delle proprietà:
package lancill.objectfields.pojo;

import java.util.List;

public class User extends Person{
 
 private String mail;
 private String userid;
 private List<String> contacts;
 private List<User> friends;
 
 public User(){}
 
 public User(String name){
  super(name);
 }
 
 public String getMail() {
  return mail;
 }
 public void setMail(String mail) {
  this.mail = mail;
 }
 public String getUserid() {
  return userid;
 }
 public void setUserid(String userid) {
  this.userid = userid;
 }
 public List<String> getContacts() {
  return contacts;
 }
 public void setContacts(List<String> contacts) {
  this.contacts = contacts;
 }
 public List<User> getFriends() {
  return friends;
 }
 public void setFriends(List<User> friends) {
  this.friends = friends;
 }
}

La classe User estende la classe Person qui di seguito:
package lancill.objectfields.pojo;

public class Person {
 
 private String name;
 private String surname;
 private String age;
 
 public Person(){}
 
 public Person(String name) {
  this.name=name;
 }
 public String getName() {
  return name;
 }
 public void setName(String name) {
  this.name = name;
 }
 public String getSurname() {
  return surname;
 }
 public void setSurname(String surname) {
  this.surname = surname;
 }
 public String getAge() {
  return age;
 }
 public void setAge(String age) {
  this.age = age;
 }

}

Partiamo con un semplice algoritmo per recuperare i campi base dell'oggetto User; perfezioneremo poi l'algoritmo per recuperare i campi ereditati e gli oggetti contenuti nelle liste.
import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;

import lancill.objectfields.pojo.User;

public class PrintBaseObjectFields {

 public static void main(String[] args) {

  User user = getUser();
  printBaseUserField(user, "user");
 }

 
 private static void printBaseUserField(Object object, String aFieldName) {

  String parentFieldName = aFieldName;

  try {
   //recupero tutti i campi della classe del mio ogetto
   Field[] classFields = object.getClass().getDeclaredFields();
 
   for (Field field : classFields) {
    //imposto il campo accessibile altrimenti JAva solleva una eccezione
    field.setAccessible(true);
 
    String fieldName = field.getName();
    Object value;
    try {
     //recupero il valore del campo per l'istanzia della classe
     value = field.get(object);
     System.out.println("FIELD UTENTE: Nome attributo: "
       + parentFieldName + "." + fieldName + ", Valore attributo: "
       + value);
    } catch (Exception e) {
     System.out.println("Impossobile recuperare una proprieta'");
     e.printStackTrace();
    }
 
   }
  } catch (Exception e) {
   System.out.println("Errore recupero field di "+aFieldName);
   e.printStackTrace();
  }

 }
 
 private static User getUser() {
  User user = new User();
  user.setName("Peter");
  user.setSurname("Pan");
  user.setAge("12");
  user.setMail("peterpan@isolachenonce.net");
  user.setUserid("pp");
  
  List<String> contacts = new ArrayList<>();
  contacts.add("555010101");
  contacts.add("333000000");
  
  user.setContacts(contacts);
  
  List<User> friends = new ArrayList<User>();
  friends.add(new User("Campanellino"));
  friends.add(new User("Mary"));
  
  user.setFriends(friends);
  
  return user;
 }
 
}
L'output della classe PrintBaseObjectFields sarà:
FIELD UTENTE: Nome attributo: user.mail, Valore attributo: peterpan@isolachenonce.net
FIELD UTENTE: Nome attributo: user.userid, Valore attributo: pp
FIELD UTENTE: Nome attributo: user.contacts, Valore attributo: [555010101, 333000000]
FIELD UTENTE: Nome attributo: user.friends, Valore attributo: [lancill.objectfields.pojo.User@6fafc4c2, lancill.objectfields.pojo.User@7c9ed5d6]

Come potete notare, i campi ereditati (della classe Person) non sono stati recuperati e le liste di oggetti complessi non sono stampate correttamente.


Perfezioniamo il nostro algoritmo per gestire anche i campi ereditati e le liste. 
Dovremo manipolare degli array, quindi per eseguire il prossimo algoritmo è necessario scaricare la libreria Apache Commons Lang.

Creiamo la classe PrintAllObjectFields che utilizzerà il metodo printAllUserField (diversamente da quanto fatto prima con printBaseUserField).

Il nuovo metodo questa volta fa uso della ricorsione per eseguire il parsing in profondità di tutti i tipi di campo complessi.
import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import org.apache.commons.lang3.ArrayUtils;

import lancill.objectfields.pojo.User;

public class PrintAllObjectFields {

 public static void main(String[] args) {

  User user = getUser();
  printAllUserField(user, "user");
 }

 private static void printAllUserField(Object object, String aFieldName) {

  
  String parentFieldName = aFieldName;

  try{
   
  
   Field[] classFields = object.getClass().getDeclaredFields();
 
   //definisco un identificatore delle mie classi per discriminare in seguito il tipo di parsing
   String PACKAGE = "lancill.objectfields.pojo";
   
   Field [] superClassFields;
   //Creo un array con tutti i campi dell'oogetto
   Field [] allClassFields = ArrayUtils.addAll(classFields);
   
   //Recupero i campi della classe Person
   Class superClass = object.getClass().getSuperclass();
   if(superClass.getCanonicalName().startsWith(PACKAGE)){
    superClassFields = superClass.getDeclaredFields();
    
    allClassFields = ArrayUtils.addAll(superClassFields, classFields);
   }
   
   
   for (Field field : allClassFields) {
    field.setAccessible(true);
 
    String fieldName = field.getName();
    Object value;
    try {
     //recupeo il tipo del campo
     String canonicalName =field.getType().getCanonicalName();
     
     //controllo se è un campo di un tipo primitivo Java
     if(!canonicalName.startsWith("java.util.List") && !canonicalName.startsWith(PACKAGE)){
 
      value = field.get(object);
      System.out.println("FIELD UTENTE: Nome attributo: "
        + parentFieldName + "." + fieldName + ", Valore attributo: "
        + value);
     }else{
      /**
       * Eseguo il parsing di un campo complesso:
       * Liste
       * Classi del mio package
       **/
      Object complexObj = field.get(object);
      
      if(complexObj!=null){
       if(complexObj instanceof List){//GESTIONE LISTE
        System.out.println("FIELD UTENTE: START Attributo complesso:"+parentFieldName+"."+fieldName);
        //Recupeo l'istanza della lista
        List<Object> list = (List<Object>)complexObj;
        int count=1;
        //Ciclo su tutti gli elementi della lista
        for(Object objInList : list){
         String objCanonicalName =objInList.getClass().getCanonicalName(); 
         if(!objCanonicalName.startsWith(PACKAGE)){//se il tipo dell'oggetto non è complesso
          System.out.println("FIELD UTENTE: Nome attributo: "
            + parentFieldName + "." + fieldName +".[elemento-"+count+"], Valore attributo: "
            + objInList);
         }else{
          //uso la ricorsione per eseguire gli stessi calcoli in profondita
          printAllUserField(objInList, parentFieldName+"."+fieldName+".[elemento-"+count+"]");
         }
         count++;
        }
        System.out.println("FIELD UTENTE: END Attributo complesso:"+fieldName);
        
       }else{//GESTIONE OGGETTO COMPLESSO DEL MIO PACKAGE
        System.out.println("FIELD UTENTE: START Attributo complesso:"+parentFieldName+"."+fieldName);
        //uso la ricorsione per eseguire gli stessi calcoli in profondita
        printAllUserField(complexObj, parentFieldName+"."+fieldName);
        System.out.println("FIELD UTENTE: END Attributo complesso:"+fieldName);
       }
      }
     }
    } catch (Exception e) {
     System.out.println("Impossobile recuperare una proprieta'");
     e.printStackTrace();
    }
 
   }
  } catch (Exception e) {
   System.out.println("Errore recupero field di "+aFieldName);
   e.printStackTrace();
  }

 }
 
 private static User getUser() {
  User user = new User();
  user.setName("Peter");
  user.setSurname("Pan");
  user.setAge("12");
  user.setMail("peterpan@isolachenonce.net");
  user.setUserid("pp");
  
  List<String> contacts = new ArrayList<>();
  contacts.add("555010101");
  contacts.add("333000000");
  
  user.setContacts(contacts);
  
  List<User> friends = new ArrayList<User>();
  friends.add(new User("Campanellino"));
  friends.add(new User("Mary"));
  
  user.setFriends(friends);
  
  return user;
 }
 
}

L'output della classe PrintAllObjectFields sarà:
FIELD UTENTE: Nome attributo: user.name, Valore attributo: Peter
FIELD UTENTE: Nome attributo: user.surname, Valore attributo: Pan
FIELD UTENTE: Nome attributo: user.age, Valore attributo: 12
FIELD UTENTE: Nome attributo: user.mail, Valore attributo: peterpan@isolachenonce.net
FIELD UTENTE: Nome attributo: user.userid, Valore attributo: pp
FIELD UTENTE: START Attributo complesso:user.contacts
FIELD UTENTE: Nome attributo: user.contacts.[elemento-1], Valore attributo: 555010101
FIELD UTENTE: Nome attributo: user.contacts.[elemento-2], Valore attributo: 333000000
FIELD UTENTE: END Attributo complesso:contacts
FIELD UTENTE: START Attributo complesso:user.friends
FIELD UTENTE: Nome attributo: user.friends.[elemento-1].name, Valore attributo: Campanellino
FIELD UTENTE: Nome attributo: user.friends.[elemento-1].surname, Valore attributo: null
FIELD UTENTE: Nome attributo: user.friends.[elemento-1].age, Valore attributo: null
FIELD UTENTE: Nome attributo: user.friends.[elemento-1].mail, Valore attributo: null
FIELD UTENTE: Nome attributo: user.friends.[elemento-1].userid, Valore attributo: null
FIELD UTENTE: Nome attributo: user.friends.[elemento-2].name, Valore attributo: Mary
FIELD UTENTE: Nome attributo: user.friends.[elemento-2].surname, Valore attributo: null
FIELD UTENTE: Nome attributo: user.friends.[elemento-2].age, Valore attributo: null
FIELD UTENTE: Nome attributo: user.friends.[elemento-2].mail, Valore attributo: null
FIELD UTENTE: Nome attributo: user.friends.[elemento-2].userid, Valore attributo: null
FIELD UTENTE: END Attributo complesso:friends

GitHub

Riferimenti

Commenti

Post popolari in questo blog

Arrotondamento e troncamento in Java

Eclipse: Shortcuts (scorciatoie) da tastiera

Strutture dati: List, Set, Map

Creare un eseguibile Java