Как получить список всех полей объекта (включая вложенные) с помощью рефлексии
Товарищи, который день ломаю голову как корректней вытащить все поля объекта с помощью рефлексии. Затруднение заключается в том, что я заранее не знаю какой объект ко мне придет но мне нужно получить список всех его полей включая все вложенные объекты и их поля и тд.
Пример:
public class House {
private int height;
private String houseName;
private HouseGuard houseGuard;
private Boolean isBad = false;
}
У класса HouseGuard тоже может быть в качестве поля другой объект и так несколько раз. Я понимаю, что мне необходимо рекурсивным методом пробежаться по всем полям объекта и если это поле объект, то пробежаться и по нему и тд. Но пока эксперименты заканчивались переполнением стека.
Ответы (3 шт):
Решение нашел пока писал вопрос, оставлю его тут:
public static Map<String, Object> returnFieldsWithValues(Field[] fields) {
Map<String, Object> map = null;
try {
map = new HashMap<>();
for (Field field : fields){
field.setAccessible(true);
if(!field.getType().isPrimitive() &&
!field.getType().getSimpleName().equals("String")&&
!field.getType().getSimpleName().equals("Integer")&&
!field.getType().getSimpleName().equals("Boolean")&&
!field.getType().getSimpleName().equals("Enum")&&
!field.getType().getSimpleName().equals("Double")&&
!field.getType().getSimpleName().equals("Float")&&
!field.getType().getSimpleName().equals("Long")){
map.putAll(returnFieldsWithValues(field.getType().getDeclaredFields()));
}else {
map.put(field.getName(), field.getType().getSimpleName());
}
}
} catch (SecurityException e) {
e.printStackTrace();
} catch (IllegalArgumentException e) {
e.printStackTrace();
}
return map;
}
Метод собирает все поля объекта в мапу включая поля полей и тд.
А как насчет того, что в двух классах может быть поле с одинаковым названием?) А если класс наследуется от другого класса (это о getDeclaredFields)
Ну а в целом можно заюзать commons-lang3, например:
public static List<Field> openCls(Class<?> cls) {
List<Field> fields = new ArrayList<>();
for(Field field : FieldUtils.getAllFieldsList(cls)) {
if(ClassUtils.isPrimitiveOrWrapper(field.getType())) {
fields.add(field);
}
else {
fields.addAll(openCls(field.getType()));
}
}
return fields;
}
Немного подумав, а также благодаря совету lampa набросал следующий алгоритм который собирает мапу из объектов в качестве ключа и коллекции полей в качестве значения. По идее должно работать при любом кол-ве вложенных объектов.
private static Map<Object,List<Field>> openCls(Object targetObject) throws IllegalAccessException {
Map<Object,List<Field>> map = new HashMap<>();
for(Field field : FieldUtils.getAllFieldsList(targetObject.getClass())) {
field.setAccessible(true);
if(ClassUtils.isPrimitiveOrWrapper(field.getType())) {
map.put(targetObject,FieldUtils.getAllFieldsList(targetObject.getClass()));
}
else {
map.putAll(openCls(field.get(targetObject)));
}
}
return map;
}
В таком случае мы получаем все объекты (включая вложенные) а также список всех их полей, что позволяет нам доставать значения из них при помощи field.get(Object)
Например ищу поле под названием "level" и получаю его значение
Map<Object,List<Field>> map = openCls(house);
for (Map.Entry<Object,List<Field>> entry: map.entrySet()) {
for(Field field:entry.getValue()){
field.setAccessible(true);
if(field.getName().equals("level")){
Object objectValue = field.get(entry.getKey());
System.out.println(objectValue.toString());
}
}
}