- PVSM.RU - https://www.pvsm.ru -

Spring-Jackson + @JsonView: фильтруем JSON

Spring-Jackson + @JsonView: фильтруем JSON - 1Здравствуйте!

Недавно в проекте Spring столкнулся с задачей катомизации сериализации объекта User в JSON в зависимости от контроллера: для REST API контроллера нужно было возвращать хешированный пароль (поле user.password), а для контроллера отображения на UI- нет. Можно решить задачу в «лоб», сделав нестолько TO (Data Transfer Object [1]), но в Spring 4.2+/Jackson 2.6 появилась возможность использовать Jackson’s Serialization Views [2]. Однако с статье есть подвох, и для невнимательных читателей вьюхи работают не так, как он ожидает. В результате мне пришлось немного покопаться в реализации Jackson, чтобы понять, как все это работает. Коротко об этом:

MapperFeature.DEFAULT_VIEW_INCLUSION

В статье есть небольшое упоминание

In Spring MVC default configuration, MapperFeature.DEFAULT_VIEW_INCLUSION is set to false.

Это означает, что по умолчанию поля, не помеченные аннотацией @JsonView, исключаются. Но если посмотреть в код MapperFeature, то увидим:

    ...
    * Default value is enabled, meaning that non-annotated
    * properties are included in all views if there is no
    * {@link com.fasterxml.jackson.annotation.JsonView} annotation.
    *
    * Feature is enabled by default.
    */
    DEFAULT_VIEW_INCLUSION(true),

Т.е все с точностью до наоборот — все, что непомечено, включается. И если пометить только нужные для UI поля User:

public class User
    ...
    @JsonView(View.UI.class)
    protected String email;

    @JsonView(View.UI.class)
    protected boolean enabled = true;

    protected String password;

и вызвать помеченный @JsonView метод контроллера

@JsonView(View.UI.class)
@RequestMapping(value = "/{id}", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE)
public User get(@PathVariable("id") int id) {
   return ...;
}

то в результат войдут как помеченные поля User (email, enabled, ..), так и все остальные (password).

FilteredBeanPropertyWriter

Т.к. хочется исключить из контроллера UI только одно поле password, логично будет пометить только его. Смотрим в код jackson-databind-2.8.0: если запрос контроллера и поля его результата аннотированы @JsonView, Jackson сериализует через FilteredBeanPropertyWriter.serializeAsField

    final Class<?> activeView = prov.getActiveView();
    if (activeView != null) {
        int i = 0, len = _views.length;
        for (; i < len; ++i) {
           if (_views[i].isAssignableFrom(activeView)) break;
        }
        // not included, bail out:
        if (i == len) {
            _delegate.serializeAsOmittedField(bean, jgen, prov);
            return;
        }
    }
    _delegate.serializeAsField(bean, jgen, prov);

Т.е. если View, которым помечено поле объекта, совпадает или является суперклассом от View метода контроллера, поле сериализуется. Иначе оно пропускается (serializeAsOmittedField).

Решение

В итоге:

  • создаем по одному View для каждого контекста сериализации
    public class View {
        public static class REST {}
        public static class UI {}
    }
  • помечаем в User исключаемые в UI поля тем View, в котором они должны присутствовать (REST)
    public class User
        ...
    
        protected String email;
    
        protected boolean enabled = true;
    
        @JsonView(View.REST.class)
        protected String password;
  • аннотируем метод контроллера UI соответствующим контекстом
    @JsonView(View.UI.class)
    @RequestMapping(value = "/{id}", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE)
    public User get(@PathVariable("id") int id) {
       return ...;
    }

Теперь поле password в результат не попадет. В контроллере REST можно обойтись без @JsonView, т.к. туда включаются все поля User.

Спасибо за внимание! Надеюсь @JsonView сделают Ваши Spring приложения более красивыми и компактными.

Автор: gkislin

Источник [3]


Сайт-источник PVSM.RU: https://www.pvsm.ru

Путь до страницы источника: https://www.pvsm.ru/java/169862

Ссылки в тексте:

[1] Data Transfer Object: https://ru.wikipedia.org/wiki/DTO

[2] Jackson’s Serialization Views: https://spring.io/blog/2014/12/02/latest-jackson-integration-improvements-in-spring#json-views

[3] Источник: https://habrahabr.ru/post/307392/?utm_source=habrahabr&utm_medium=rss&utm_campaign=best