jackson - не сериализовать ленивые объекты

У меня есть сущность:

@Entity
public class Book {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private long id;

    @Column
    private String title;

    @OneToMany(fetch = FetchType.LAZY, mappedBy = ("movie"),cascade = CascadeType.ALL)
    private List<Genre> genre;

}

Затем у меня есть контроллер, предназначенный для извлечения книг, моя проблема в том, что поле жанра включается в ответ json моего контроллера. В любом случае я могу исключить те поля, которые лениво загружаются, когда Джексон сериализует объект?

Это конфигурация моего ObjectMapper:

Hibernate4Module hm = new Hibernate4Module();
hm.configure(Hibernate4Module.Feature.FORCE_LAZY_LOADING, false);
registerModule(hm);
configure(SerializationFeature.INDENT_OUTPUT, true);

Спасибо!

Я не могу пометить его как JsonIgnore, так как он навсегда исчезнет из коробки сериализации. Бывают случаи, когда мне нужно будет получить жанры вместе с книгой, и к тому времени я буду использовать "fetch join" в своем запросе, чтобы он не был нулевым.


person villager    schedule 18.09.2014    source источник
comment
[Transient] [1] [1]: stackoverflow.com/questions/20700530/ Попробуйте использовать эту тему, чтобы получить свой ответ;)   -  person Fincio    schedule 18.09.2014
comment
Привет, @Fincio, я не могу сделать поле жанра временным, поскольку мне нужно, чтобы оно сохранялось в базе данных.   -  person villager    schedule 18.09.2014
comment
Хммм, а как насчет @JsonIgnoreProperties ({genre}) или @JsonIgnore   -  person Fincio    schedule 18.09.2014
comment
Я обновил свой пост, почему я не могу этого сделать. Спасибо   -  person villager    schedule 18.09.2014
comment
Вы нашли какое-нибудь решение для этого? Я столкнулся с той же проблемой. пожалуйста, помогите, если у вас есть.   -  person Ilesh Patel    schedule 13.07.2016
comment
@IleshPatel, принятый ниже ответ (Давиде Росси) решил мою проблему.   -  person villager    schedule 14.07.2016
comment
@ yin03 удаляет только пустую коллекцию, но что, если мне не нужна коллекция с данными при начальной загрузке, например stackoverflow.com/questions/38344029/   -  person Ilesh Patel    schedule 14.07.2016


Ответы (5)


Вы можете сделать это с помощью аннотации Jackson @JsonInclude.

Согласно javadoc последней версии последней версии (прямо сейчас 2.4) вы можете указать с помощью простой аннотации, следует ли включать аннотированное свойство, если значение поля пустое или пустое.

По умолчанию это JsonInclude.Include.ALWAYS, и это означает, что даже если ваши лениво незагруженные значения равны нулю, Джексон включает свойство.

Указание не включать пустые или нулевые значения может значительно уменьшить размер ответа JSON со всеми включенными преимуществами.

Если вы хотите изменить это поведение, вы можете добавить аннотацию на уровне класса или на уровне отдельного свойства / getterMethod.

Попробуйте добавить следующие аннотации к полям, которые вы не хотите включать, если они пусты:

@JsonInclude(JsonInclude.Include.NON_EMPTY)
@OneToMany(fetch = FetchType.LAZY, mappedBy = ("movie"),cascade = CascadeType.ALL)
private List<Genre> genre;
person Davide Rossi    schedule 29.09.2014
comment
Это не работает для меня в решении Spring JPA + Spring REST Controller. Ленивая выборка все еще происходит, когда происходит сериализация JSON ... - person Ronye Vernaes; 26.02.2015
comment
Это неправильный ответ для JPA 2.1 в сочетании с EclipseLink! Проблема, очевидно, в том, что Джексон проверяет свойства на NON_EMPTY, вызывая методы получения, и это вызывает отложенную загрузку! К сожалению, нет аннотации AccesType Field Json или чего-то в этом роде, что могло бы помочь. Я думаю, это, кстати, поддерживается Moxy (я думаю, что для этого используются аннотации JAXB). - person Nabi; 26.02.2016
comment
@RonyeVernaes у меня работает после добавления Hibernate5Module Bean. См. Ответ chrismarx в stackoverflow.com/questions/21708339/. - person Zeemee; 02.01.2017
comment
Я могу подтвердить, что это работает с дополнительной информацией Zeemee. В Spring Boot вам нужно добавить модуль Jackson Hibernate5 в свой pom.xml и зарегистрировать bean-компонент. - person jayb0b; 22.02.2019

Вы можете использовать функцию фильтра JSON Джексона:

@Entity
@JsonFilter("Book") 
public class Book {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private long id;

    @Column
    private String title;

    @OneToMany(fetch = FetchType.LAZY, mappedBy = ("movie"),cascade = CascadeType.ALL)
    private List<Genre> genre;
} 

@Entity
@JsonFilter("Genre")
public class Genre {
   ...
}

Затем в Контроллере вы указываете, что фильтровать:

@Controller
public class BookController {
      @Autowired
      private ObjectMapper objectMapper;

      @Autowird
      private BookRepository bookRepository;

       @RequestMapping(value = "/book", method = RequestMethod.GET, produces =  "application/json")
       @ResponseBody
       public ResponseEntity<String> getBooks() {

          final List<Book> books = booksRepository.findAll();
          final SimpleFilterProvider filter = new SimpleFilterProvider();
          filter.addFilter("Book", SimpleFilterProvider.serializeAllExcept("Genre");
          return new ResponseEntity<>(objectMapper.writer(filter).writeValueAsString(books), HttpStatus.OK)
       }

}

Таким образом, вы можете контролировать, хотите ли вы фильтровать ленивое отношение во время выполнения.

person Ricardo Veguilla    schedule 18.09.2014
comment
Поскольку очередь редактирования сейчас заполнена, я должен исправить одну строку; вторым аргументом addFilter () должен быть SimpleBeanPropertyFilter .serializeAll ... не SimpleFilterProvider .serializeAll ... класс - person Yasin Okumuş; 20.04.2017
comment
Мне пришлось использовать жанр имени поля в классе Book, а не @JsonFilter (Genre), чтобы исключить список из моей сериализации. - person Vincent B; 21.06.2021

Возможно, это связано с известной проблемой отложенной загрузки.

Я не использую jackson-datatype-hibernate, но для решения той же проблемы я получил постоянную коллекцию из изображения с помощью DTO вместо прямой сериализации объекта Hibernate. Такие инструменты, как Dozer, могут вам в этом помочь. В качестве альтернативы я написал небольшую утилиту для выполнения подобных сопоставлений.

Если вы просто хотите поэкспериментировать с тем, как может выглядеть DTO, вы можете заменить выгруженную постоянную коллекцию на обычную пустую коллекцию, например books.setGenre (new ArrayList ‹> ()); К сожалению, я не знаю способа узнать, загружен ли лениво загруженный объект или нет, поэтому вы не можете выполнить это переназначение автоматически. Места, в которых вы заменяете постоянные коллекции, должны определяться вами в индивидуальном порядке.

person Jay    schedule 18.09.2014

Вы можете использовать конфигурацию пружины, чтобы отключить принудительную отложенную загрузку по умолчанию!

@Configuration
public class JacksonConfig {
    
    @Bean
    public ObjectMapper objectMapper() {
        ObjectMapper mapper = new ObjectMapper();
        Hibernate5Module hibernate5Module = new Hibernate5Module();
        hibernate5Module.configure(Feature.FORCE_LAZY_LOADING, false);
        // Enable below line to switch lazy loaded json from null to a blank object!
        //hibernate5Module.configure(Feature.SERIALIZE_IDENTIFIER_FOR_LAZY_NOT_LOADED_OBJECTS, true);
        mapper.registerModule(hibernate5Module);
        return mapper;
    }
}
person zbishop    schedule 18.12.2020

Вы можете использовать Gson вместо ObjectMapper и при определении объекта пометить поле как «временное».

public class Book {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private long id;

    @Column
    private String title;

    @OneToMany(fetch = FetchType.LAZY, mappedBy = ("movie"),cascade = CascadeType.ALL)
    private **transient** List<Genre> genre;

}

при десериализации с использованием gson.toJson(book) Gson не будет десериализовать этот элемент.

person Mehul Gupta    schedule 08.06.2020