Jackson/Hibernate, методы мета-получения и сериализация

У нас есть много спящих классов сущностей с вложенными отношениями. Я пытаюсь найти лучший способ преобразовать данный объект в эквивалентный формат json.

Я знаю о JsonIgnore, примесях Джексона и JsonFilters и экспериментировал с ними.

Проблемы, с которыми мы сталкиваемся, следующие

  1. Объекты, связанные друг с другом с помощью OneToMany/JoinColumn или подобных аннотаций, создают бесконечную рекурсию.

  2. Утилиты или метаметоды. Джексон, похоже, использует методы геттера, а не поля. Некоторые из методов являются «мета»-методами, не связанными ни с какими столбцами. Примером метода getTotal может быть суммирование значений пары фактических полей без фактического итогового поля. Аналогичный случай для других случаев, таких как getIncomeAccounts, который фильтрует учетные записи на основе некоторых критериев.

Фильтр Джексона, который я написал, немного помогает - он проверяет, существует ли поле класса на основе имени свойства Джексона. Он также проверяет наличие аннотаций, таких как аннотация JoinColumn, чтобы избежать рекурсии, если поле существует.

Есть ли способ получить метаданные из спящего режима и использовать их в своих фильтрах? В основном для данного объекта сущности мне интересно знать, будет ли свойство, которое Джексон хочет сериализовать, сопоставляться со столбцом и сериализоваться только в том случае, если с ним связано значение столбца. Hibernate, безусловно, знает о свойствах и сопоставлениях столбцов.

Опции Mixins и jsonignore работают, но тогда мы зависим от отдельного разработчика, который помнит о размещении аннотаций в соответствующем месте. Обычно забытые аннотации обнаруживаются слишком поздно, когда мы действительно хотим получить экспортированные данные, чтобы проанализировать какую-то проблему и создать тестовый пример локально.


person user871199    schedule 27.03.2019    source источник
comment
Обходным путем может быть создание дублирующегося класса с единственной целью сериализации, который содержит только нужные поля и, например. скопируйте все из объекта в dto, используя отражение   -  person Felk    schedule 27.03.2019
comment
Был ли ответ ниже полезен?   -  person Michał Ziober    schedule 01.08.2020


Ответы (2)


Обычно я сопоставляю Entities с DTO вручную или с помощью таких инструментов, как MapStruct.
Это даст вам максимальную гибкость, к сожалению, с небольшими накладными расходами в начале.
Однако со временем вы увидите, что оно того стоило.

Jackson, GSON и другие инструменты сериализации, очевидно, ограничены в том, что они могут делать «из коробки», и такая настройка требует слишком много работы, имхо, а также сложна для понимания и обслуживания.

Будь проще.

person LppEdd    schedule 27.03.2019

Если вы не хотите создавать новую модель POJO для представления JSON на уровне REST API, вам необходимо подготовить модель ORM перед переходом на уровень Jackson.

Включить HibernateXModule

Вы должны начать с включения Hibernate модуля, который лучше всего подходит для вашей Hibernate версии. Это решает многие проблемы с lazy-loadings и внутренними типами данных.

Двунаправленные отношения

Прочитайте о вариантах, которые Jackson есть для решения проблемы циклов при сериализации . Основные аннотации:

Определите правильную видимость

Вы можете определить глобальную видимость на ObjectMapper, как указать jackson использовать только поля — желательно глобально и при необходимости настраивать их для данного класса, используя JsonAutoDetect аннотация.

Посмотреть POJO модель

Вероятно, в большинстве случаев вы сможете повторно использовать модель POJO, созданную для ORM. В тех случаях, когда настройка вывода JSON с аннотацией будет затруднена, вы всегда можете создать собственный класс и сопоставить модель ORM с этой моделью вручную на дополнительном уровне сопоставления/бизнес-уровня.

Настройка сериализаторов

Если вам нужно обрабатывать некоторые пользовательские аннотации или некоторые поля в целом, вы можете использовать BeanSerializerModifier и BeanPropertyWriter. Его не просто реализовать, но он очень мощный. См. пример использования здесь: настраиваемая сериализация и десериализация Джексона.

Простой пример того, как это можно сделать для двунаправленных отношений и настройки видимости:

import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.JsonIdentityInfo;
import com.fasterxml.jackson.annotation.ObjectIdGenerators;
import com.fasterxml.jackson.databind.ObjectMapper;

import java.io.File;
import java.util.Arrays;
import java.util.List;

public class JsonApp {

    public static void main(String[] args) throws Exception {
        File jsonFile = new File("./resource/test.json").getAbsoluteFile();

        Item item0 = new Item();
        item0.setId(1);
        item0.setItemName("Item 0");

        Item item1 = new Item();
        item1.setId(2);
        item1.setItemName("Item 1");

        List<Item> items = Arrays.asList(item0, item1);
        User user = new User();
        user.setId(123);
        user.setName("Rick");
        user.setUserItems(items);
        items.forEach(item -> item.setOwner(user));

        ObjectMapper mapper = new ObjectMapper();
        mapper.setVisibility(mapper.getSerializationConfig().getDefaultVisibilityChecker()
                .withFieldVisibility(JsonAutoDetect.Visibility.ANY)
                .withGetterVisibility(JsonAutoDetect.Visibility.NONE)
                .withSetterVisibility(JsonAutoDetect.Visibility.NONE)
                .withCreatorVisibility(JsonAutoDetect.Visibility.NONE));

        System.out.println(mapper.writeValueAsString(user));
    }
}

@JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "id")
class User {

    private int id;
    private String name;
    private List<Item> userItems;

    // getters, setters
}

@JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "id")
class Item {

    private int id;
    private String itemName;
    private User owner;

    // getters, setters
}

Над кодом печатается:

{"id":123,"name":"Rick","userItems":[{"id":1,"itemName":"Item 0","owner":123},{"id":2,"itemName":"Item 1","owner":123}]}
person Michał Ziober    schedule 27.03.2019