Объект в объекте в HashMap с отражением

Проект:

В настоящее время я использую инструмент под названием Нетуно для разработки API, поэтому для экспорта объектов в json необходимо выполнить преобразование объекта в HashMap, для этого в родительском классе был разработан метод для экспорта объекта.

The method:
   public Map<String, Object> export() {
        Object obj = this;
        Map<String, Object> map = new HashMap<>();
        for (Field field : obj.getClass().getDeclaredFields()) {
            field.setAccessible(true);
            try {
                map.put(field.getName(), field.get(obj));
            } catch (Exception e) {
                //todo e
            }
        }
        return map;
    }

Это работает, но только для простых объектов.

Моя проблема:

Если у объекта есть сложные объекты внутри моего метода, он также не может экспортировать их в HashMap.

Пример структуры:

public abstract class Master {
 public Map < String, Object >
  export () {
   Object obj = this;
   Map < String, Object > map = new HashMap < > ();
   for (Field field: obj.getClass().getDeclaredFields()) {
    field.setAccessible(true);
    try {
     map.put(field.getName(), field.get(obj));
    } catch (Exception e) {
     //todo e
    }
   }
   return map;
  }
}

public class Foo extends Master {
 private int a;
 private int b;
 private String c;
 private Bar bar;
//...
}

public class Bar extends Master {
 private int q;
 private int w;
 private String e;
//...
}

Я использую это следующим образом:

return new Bar(/*data*/).export();

Выход:

{
  "a": 2,
  "b": 5,
  "c": "abc",
  "bar": "myproject.myPackage.Bar@XXXXX"
}

Ожидаемый результат:

{
  "a": 2,
  "b": 5,
  "c": "abc",
  "bar": {
    "q": 10,
    "w": 15,
    "e": "it works"
  }
}

Существуют библиотеки, которые также дадут вам желаемый результат (сериализация объекта в JSON/десериализация JSON в объект), например, Gson/Jackson/много других, может быть, что-то посмотреть?

Mark 20.05.2019 20:44

Вы можете добавить условие, если field.get(obj) является экземпляром Master cast to master и поместить результат метода экспорта карты, но, как отметил Марк, существуют библиотеки с этой функциональностью.

imperezivan 20.05.2019 21:07

@Mark Спасибо за библиотеки :), но я старался не использовать их, чтобы не нарушать стандарт моего фреймворка.

Filipe 21.05.2019 17:28
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
2
3
1 201
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Вам нужно решить, должно ли значение быть указано на карте просто или рекурсивно. Поэтому вы можете использовать простой метод, подобный этому:

private boolean isSimpleType(Class<?> type) {
    return type.isPrimitive() ||
            Boolean.class == type ||
            Character.class == type ||
            CharSequence.class.isAssignableFrom(type) ||
            Number.class.isAssignableFrom(type) ||
            Enum.class.isAssignableFrom(type);
}

Этот метод возвращает true для всех примитивных типов или объектов-оболочек, строк и перечислений. Вы можете просто настроить эти методы в соответствии с вашими потребностями. Подробнее о том, как определить, является ли класс простым, см. здесь.

Теперь вы можете использовать для преобразования объекта:

public Map<String, Object> convert(Object object) {
    Map<String, Object> map = new HashMap<>();
    for (Field field : object.getClass().getDeclaredFields()) {
        field.setAccessible(true);
        try {
            if (isSimpleType(field.getType())) {
                map.put(field.getName(), field.get(object));
            } else {
                map.put(field.getName(), convert(field.get(object)));
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    return map;
}

Вы также можете настроить это, чтобы использовать свой export метод.

Результат для вашего примера будет таким:

{a=2, b=5, bar = {q=10, e=it works, w=15}, c=abc}

Кроме того, я бы настоятельно рекомендовал использовать библиотеку (например, Джексон), которая уже делает подобные вещи в совершенстве. Вот то же решение с использованием Джексона:

Map result = new ObjectMapper().convertValue(object, Map.class);

Или, если вам нужна безопасность типов:

ObjectMapper mapper = new ObjectMapper();
MapType mapType = mapper.getTypeFactory().constructMapType(Map.class, String.class, Object.class);
Map<String, Object> result = mapper.convertValue(value, mapType);

Другие вопросы по теме