Научитесь преобразовывать объект в Java Map, используя различные способы, предоставляемые Jackson и Gson API. Также можно использовать создание собственного решения с помощью Java Reflection, но не рекомендуется изобретать велосипед, пока предоставленные решения не перестанут соответствовать вашим целям.
1. Введение
В этом уроке мы преобразуем экземпляр следующего класса Employee в Map. Класс Employee имеет простые типы, такие как String, и новые типы Java, такие как LocalDate и Collection.
У нас есть Список типов ролей для дальнейшей демонстрации поведения различных решений. Мы увидим, как различные решения преобразуют вложенные типы в преобразованной карте.
class Employee {private Integer id;private String name;private LocalDate dateOfBirth;private List<String> locations;private List<Role> roles;}class Role {private Integer id;private String name;}
2. Использование Джексона
Jackson — многоцелевая библиотека, которая очень хорошо поддерживает различные типы преобразований, такие как JSON или XML. Jackson также поддерживает преобразование объекта в карту следующими способами:
2.1 Использование ObjectMapper.convertValue()
Метод convertValue() выполняет двухэтапное преобразование из заданного значения в экземпляр заданного типа значения. Сначала он сериализует заданное значение в JSON, а затем привязывает данные JSON к значению заданного типа. Но преобразование более эффективно, поскольку полная сериализация не происходит(необходима).
Обратите внимание, что в следующем примере мы регистрируем класс JavaTimeModule, поскольку Jackson по умолчанию не поддерживает новые классы даты и времени Java 8.
Employee employee = new Employee(1, "Alex",LocalDate.of(1995, 1, 2),List.of("Delhi", "Nevada"),List.of(new Role(11, "Finance"), new Role(12, "HR")));System.out.println(convertObjectToMapUsingObjectMapper(employee));//The conversion methodstatic Map<String, String> convertObjectToMapUsingObjectMapper(Employee employee) {ObjectMapper objectMapper = new ObjectMapper();objectMapper.registerModule(new JavaTimeModule());objectMapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd"));return objectMapper.convertValue(employee, Map.class);}
Вывод программы.
{id=1,name=Alex,dateOfBirth=1995-01-02,locations=[Delhi, Nevada],roles=[{id=11, name=Finance},{id=12, name=HR}]}
Обратите внимание, что этот метод преобразует связанные и вложенные классы(например, Role) также в LinkedHashMap.

2.2 Использование JavaPropsMapper для преобразования в свойства
Другое интересное решение — преобразовать Object в Properties. Свойства имеют плоскую структуру. Даже вложенные структуры и коллекции преобразуются в плоскую структуру, а все поля/значения преобразуются в String. В некоторых случаях это может быть хорошим решением.
Employee employee = new Employee(1, "Alex",LocalDate.of(1995, 1, 2),List.of("Delhi", "Nevada"),List.of(new Role(11, "Finance"), new Role(12, "HR")));System.out.println(convertObjectToMapUsingJavaPropsMapper(employee));//The conversion methodstatic Properties convertObjectToMapUsingJavaPropsMapper(Employee employee) throws IOException {JavaPropsMapper mapper = new JavaPropsMapper();mapper.registerModule(new JavaTimeModule());mapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd"));Properties properties = mapper.writeValueAsProperties(employee);return properties;}
Вывод программы.
{id=1,name=Alex,dateOfBirth=1995-01-02,locations.1=Delhi,locations.2=Nevada,roles.1.id=11,roles.1.name=Finance,roles.2.id=12,roles.2.name=HR}
Давайте проверим иерархию объектов в отладчике.

3. Использование TypeToken от Gson
Если в нашем проекте уже есть зависимость Gson в приложении, мы можем рассмотреть возможность использования Gson.fromJson() для преобразования объекта в JSON и преобразования JSON в HashMap на втором этапе.
Эта техника использует полную сериализацию и десериализацию, поэтому она может не дать лучшей производительности, чем Jackson. Использовать Gson, когда использование Jackson невозможно по какой-то причине.
Employee employee = new Employee(1, "Alex",LocalDate.of(1995, 1, 2),List.of("Delhi", "Nevada"),List.of(new Role(11, "Finance"), new Role(12, "HR")));System.out.println(convertObjectToMapUsingGson(employee));static Map<String, String> convertObjectToMapUsingGson(Employee employee) {Gson gson = new GsonBuilder().registerTypeAdapter(LocalDate.class, new LocalDateAdapter()).create();return gson.fromJson(gson.toJson(employee),new TypeToken<HashMap<String, Object>>() {}.getType());}
Вывод программы.
{id=1.0,name=Alex,dateOfBirth=1995-01-02,locations=[Delhi, Nevada],roles=[{id=11.0, name=Finance},{id=12.0, name=HR}]}
Между преобразованием с использованием Gson и Jackson есть два основных различия.
- По умолчанию Gson преобразует все числовые значения в тип Double.
- Gson использует LinkedTreeMap для вложенных классов вместо LinkedHashMap, используемого Jackson.

4. Использование отражения
Другой возможный метод, хотя и не рекомендуемый, — это рефлексия. Используя рефлексию, мы должны написать всю логику самостоятельно, и поэтому есть вероятность ошибок. Если вы примете этот подход, обязательно тщательно протестируйте его перед тем, как внедрять в производство.
Ниже приведен очень простой фрагмент для простого класса POJO, который полагается на получение значения поля по его имени. Мы можем настроить метод для извлечения значения по его геттеру, если есть такое требование.
public static Map<String, Object> toKeyValuePairs(Object instance) {return Arrays.stream(Employee.class.getDeclaredFields()).collect(Collectors.toMap(Field::getName,field -> {try {Object result = null;field.setAccessible(true);result = field.get(instance);return result != null ? result : "";} catch(Exception e) {return "";}}));}
Вывод программы.
{id=1,name=Alex,dateOfBirth=1995-01-02,locations=[Delhi, Nevada],roles=[Role(id=11, name=Finance),Role(id=12, name=HR)]}
Мы можем проверить структуру карты на следующей диаграмме. Она имеет вложенные классы нетронутыми, потому что мы не обращаемся к ним и не обрабатываем их в нашей логике. Если вам нужно обработать их в вашем приложении, то дополнительно расширьте логику в методе toKeyValuePairs().

5. Заключение
Этот урок Java научил нас преобразовывать заданный объект Java в Map с помощью различных решений. Рассматривая все решения, Jackson кажется лучшим решением с точки зрения простоты и производительности и является рекомендуемым подходом в большинстве случаев.
Для создания плоских структур мы можем преобразовать объект в свойства с помощью JavaPropsMapper.
Использование Gson очень похоже на Jackson, но не дает никаких дополнительных преимуществ, а все числа преобразуются в Double, что может быть нежелательной ситуацией в некоторых случаях. Поэтому используйте его, когда вы не можете использовать Jackson.
Наконец, рефлексия дает вам полный контроль, и вы должны самостоятельно прописать все возможные варианты использования/условия.