- PVSM.RU - https://www.pvsm.ru -
Здравствуйте!
Недавно в проекте 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, чтобы понять, как все это работает. Коротко об этом:
В статье есть небольшое упоминание
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
).
Т.к. хочется исключить из контроллера 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
).
В итоге:
public class View {
public static class REST {}
public static class UI {}
}
public class User
...
protected String email;
protected boolean enabled = true;
@JsonView(View.REST.class)
protected String password;
@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
Нажмите здесь для печати.