«1. Обзор
В этом уроке мы подробно рассмотрим аннотации Джексона.
Мы увидим, как использовать существующие аннотации, как создавать собственные и, наконец, как их отключить.
2. Аннотации сериализации Джексона
Сначала мы рассмотрим аннотации сериализации.
2.1. @JsonAnyGetter
Аннотация @JsonAnyGetter обеспечивает гибкость использования поля карты в качестве стандартных свойств.
Например, сущность ExtendableBean имеет свойство name и набор расширяемых атрибутов в виде пар ключ/значение:
public class ExtendableBean {
public String name;
private Map<String, String> properties;
@JsonAnyGetter
public Map<String, String> getProperties() {
return properties;
}
}
Когда мы сериализуем экземпляр этой сущности, мы получаем все ключи-значения в Map в качестве стандартных простых свойств:
{
"name":"My bean",
"attr2":"val2",
"attr1":"val1"
}
Вот как сериализация этого объекта выглядит на практике:
@Test
public void whenSerializingUsingJsonAnyGetter_thenCorrect()
throws JsonProcessingException {
ExtendableBean bean = new ExtendableBean("My bean");
bean.add("attr1", "val1");
bean.add("attr2", "val2");
String result = new ObjectMapper().writeValueAsString(bean);
assertThat(result, containsString("attr1"));
assertThat(result, containsString("val1"));
}
Мы также можем использовать необязательный аргумент, включенный как false, чтобы отключить @JsonAnyGetter(). В этом случае карта будет преобразована в формат JSON и появится в переменной свойств после сериализации.
2.2. @JsonGetter
Аннотация @JsonGetter является альтернативой аннотации @JsonProperty, которая помечает метод как метод получения.
В следующем примере мы указываем метод getTheName() как метод получения свойства name сущности MyBean:
public class MyBean {
public int id;
private String name;
@JsonGetter("name")
public String getTheName() {
return name;
}
}
Вот как это работает на практике:
@Test
public void whenSerializingUsingJsonGetter_thenCorrect()
throws JsonProcessingException {
MyBean bean = new MyBean(1, "My bean");
String result = new ObjectMapper().writeValueAsString(bean);
assertThat(result, containsString("My bean"));
assertThat(result, containsString("1"));
}
2.3. @JsonPropertyOrder
Мы можем использовать аннотацию @JsonPropertyOrder, чтобы указать порядок свойств при сериализации.
Давайте установим собственный порядок свойств объекта MyBean:
@JsonPropertyOrder({ "name", "id" })
public class MyBean {
public int id;
public String name;
}
Вот результат сериализации:
{
"name":"My bean",
"id":1
}
Затем мы можем выполнить простой тест:
@Test
public void whenSerializingUsingJsonPropertyOrder_thenCorrect()
throws JsonProcessingException {
MyBean bean = new MyBean(1, "My bean");
String result = new ObjectMapper().writeValueAsString(bean);
assertThat(result, containsString("My bean"));
assertThat(result, containsString("1"));
}
Мы можем также используйте @JsonPropertyOrder(alphabetic=true) для упорядочения свойств в алфавитном порядке. В этом случае вывод сериализации будет:
{
"id":1,
"name":"My bean"
}
2.4. @JsonRawValue
Аннотация @JsonRawValue может указать Джексону сериализовать свойство точно так, как оно есть.
В следующем примере мы используем @JsonRawValue для встраивания некоторого пользовательского JSON в качестве значения сущности:
public class RawBean {
public String name;
@JsonRawValue
public String json;
}
Результат сериализации сущности:
{
"name":"My bean",
"json":{
"attr":false
}
}
Далее простой тест:
@Test
public void whenSerializingUsingJsonRawValue_thenCorrect()
throws JsonProcessingException {
RawBean bean = new RawBean("My bean", "{\"attr\":false}");
String result = new ObjectMapper().writeValueAsString(bean);
assertThat(result, containsString("My bean"));
assertThat(result, containsString("{\"attr\":false}"));
}
Мы также можем использовать необязательное логическое значение аргумента, которое определяет, активна эта аннотация или нет.
2.5. @JsonValue
@JsonValue указывает единственный метод, который библиотека будет использовать для сериализации всего экземпляра.
Например, в перечислении мы аннотируем getName с помощью @JsonValue, чтобы любой такой объект был сериализован по его имени:
public enum TypeEnumWithValue {
TYPE1(1, "Type A"), TYPE2(2, "Type 2");
private Integer id;
private String name;
// standard constructors
@JsonValue
public String getName() {
return name;
}
}
Теперь вот наш тест:
@Test
public void whenSerializingUsingJsonValue_thenCorrect()
throws JsonParseException, IOException {
String enumAsString = new ObjectMapper()
.writeValueAsString(TypeEnumWithValue.TYPE1);
assertThat(enumAsString, is(""Type A""));
}
2.6. @JsonRootName
Аннотация @JsonRootName используется, если обертка включена, чтобы указать имя используемой корневой оболочки.
Обертывание означает, что вместо сериализации пользователя в что-то вроде:
{
"id": 1,
"name": "John"
}
он будет обернут следующим образом:
{
"User": {
"id": 1,
"name": "John"
}
}
Итак, давайте рассмотрим пример. Мы собираемся использовать аннотацию @JsonRootName, чтобы указать имя этой потенциальной сущности-оболочки:
@JsonRootName(value = "user")
public class UserWithRoot {
public int id;
public String name;
}
По умолчанию имя оболочки будет именем класса — UserWithRoot. Используя аннотацию, мы получаем более аккуратного пользователя:
@Test
public void whenSerializingUsingJsonRootName_thenCorrect()
throws JsonProcessingException {
UserWithRoot user = new User(1, "John");
ObjectMapper mapper = new ObjectMapper();
mapper.enable(SerializationFeature.WRAP_ROOT_VALUE);
String result = mapper.writeValueAsString(user);
assertThat(result, containsString("John"));
assertThat(result, containsString("user"));
}
Вот результат сериализации:
{
"user":{
"id":1,
"name":"John"
}
}
Начиная с версии Jackson 2.4, для таких форматов данных, как XML, доступно новое необязательное пространство имен аргументов. Если мы добавим его, оно станет частью полного имени:
@JsonRootName(value = "user", namespace="users")
public class UserWithRootNamespace {
public int id;
public String name;
// ...
}
Если мы сериализуем его с помощью XmlMapper, результат будет таким:
<user xmlns="users">
<id xmlns="">1</id>
<name xmlns="">John</name>
<items xmlns=""/>
</user>
2.7. @JsonSerialize
@JsonSerialize указывает пользовательский сериализатор, который следует использовать при маршаллинге объекта.
Давайте рассмотрим быстрый пример. Мы собираемся использовать @JsonSerialize для сериализации свойства eventDate с помощью CustomDateSerializer:
public class EventWithSerializer {
public String name;
@JsonSerialize(using = CustomDateSerializer.class)
public Date eventDate;
}
Вот простой пользовательский сериализатор Джексона:
public class CustomDateSerializer extends StdSerializer<Date> {
private static SimpleDateFormat formatter
= new SimpleDateFormat("dd-MM-yyyy hh:mm:ss");
public CustomDateSerializer() {
this(null);
}
public CustomDateSerializer(Class<Date> t) {
super(t);
}
@Override
public void serialize(
Date value, JsonGenerator gen, SerializerProvider arg2)
throws IOException, JsonProcessingException {
gen.writeString(formatter.format(value));
}
}
Теперь давайте воспользуемся им в тесте:
@Test
public void whenSerializingUsingJsonSerialize_thenCorrect()
throws JsonProcessingException, ParseException {
SimpleDateFormat df
= new SimpleDateFormat("dd-MM-yyyy hh:mm:ss");
String toParse = "20-12-2014 02:30:00";
Date date = df.parse(toParse);
EventWithSerializer event = new EventWithSerializer("party", date);
String result = new ObjectMapper().writeValueAsString(event);
assertThat(result, containsString(toParse));
}
3. Аннотации десериализации Джексона
Далее давайте рассмотрим аннотации десериализации Джексона.
3.1. @JsonCreator
Мы можем использовать аннотацию @JsonCreator для настройки конструктора/фабрики, используемой при десериализации.
Это очень полезно, когда нам нужно десериализовать некоторый JSON, который не совсем соответствует целевому объекту, который нам нужно получить.
«Давайте посмотрим на пример. Скажем, нам нужно десериализовать следующий JSON:
{
"id":1,
"theName":"My bean"
}
Однако в нашем целевом объекте нет поля theName, есть только поле имени. Теперь мы не хотим изменять сам объект, нам просто нужно немного больше контролировать процесс десортировки, аннотируя конструктор с помощью @JsonCreator, а также используя аннотацию @JsonProperty:
public class BeanWithCreator {
public int id;
public String name;
@JsonCreator
public BeanWithCreator(
@JsonProperty("id") int id,
@JsonProperty("theName") String name) {
this.id = id;
this.name = name;
}
}
Давайте посмотрим на это в действие:
@Test
public void whenDeserializingUsingJsonCreator_thenCorrect()
throws IOException {
String json = "{\"id\":1,\"theName\":\"My bean\"}";
BeanWithCreator bean = new ObjectMapper()
.readerFor(BeanWithCreator.class)
.readValue(json);
assertEquals("My bean", bean.name);
}
3.2. @JacksonInject
@JacksonInject указывает, что свойство получит свое значение из инъекции, а не из данных JSON.
В следующем примере мы используем @JacksonInject для внедрения идентификатора свойства:
public class BeanWithInject {
@JacksonInject
public int id;
public String name;
}
Вот как это работает:
@Test
public void whenDeserializingUsingJsonInject_thenCorrect()
throws IOException {
String json = "{\"name\":\"My bean\"}";
InjectableValues inject = new InjectableValues.Std()
.addValue(int.class, 1);
BeanWithInject bean = new ObjectMapper().reader(inject)
.forType(BeanWithInject.class)
.readValue(json);
assertEquals("My bean", bean.name);
assertEquals(1, bean.id);
}
3.3. @JsonAnySetter
@JsonAnySetter позволяет нам гибко использовать карту в качестве стандартных свойств. При десериализации свойства из JSON будут просто добавлены на карту.
Во-первых, мы будем использовать @JsonAnySetter для десериализации объекта ExtendableBean:
public class ExtendableBean {
public String name;
private Map<String, String> properties;
@JsonAnySetter
public void add(String key, String value) {
properties.put(key, value);
}
}
Это JSON, который нам нужно десериализовать:
{
"name":"My bean",
"attr2":"val2",
"attr1":"val1"
}
Тогда вот как все это связано вместе:
@Test
public void whenDeserializingUsingJsonAnySetter_thenCorrect()
throws IOException {
String json
= "{\"name\":\"My bean\",\"attr2\":\"val2\",\"attr1\":\"val1\"}";
ExtendableBean bean = new ObjectMapper()
.readerFor(ExtendableBean.class)
.readValue(json);
assertEquals("My bean", bean.name);
assertEquals("val2", bean.getProperties().get("attr2"));
}
3.4. @JsonSetter
@JsonSetter — это альтернатива @JsonProperty, которая помечает метод как метод установки.
Это невероятно полезно, когда нам нужно прочитать некоторые данные JSON, но целевой класс сущности не совсем соответствует этим данным, и поэтому нам нужно настроить процесс, чтобы он соответствовал.
В следующем примере мы укажем метод setTheName() в качестве установщика свойства name в нашей сущности MyBean:
public class MyBean {
public int id;
private String name;
@JsonSetter("name")
public void setTheName(String name) {
this.name = name;
}
}
Теперь, когда нам нужно распаковать некоторые данные JSON, это работает отлично:
@Test
public void whenDeserializingUsingJsonSetter_thenCorrect()
throws IOException {
String json = "{\"id\":1,\"name\":\"My bean\"}";
MyBean bean = new ObjectMapper()
.readerFor(MyBean.class)
.readValue(json);
assertEquals("My bean", bean.getTheName());
}
3.5. @JsonDeserialize
@JsonDeserialize указывает на использование пользовательского десериализатора.
Во-первых, мы будем использовать @JsonDeserialize для десериализации свойства eventDate с помощью CustomDateDeserializer:
public class EventWithSerializer {
public String name;
@JsonDeserialize(using = CustomDateDeserializer.class)
public Date eventDate;
}
Вот пользовательский десериализатор:
public class CustomDateDeserializer
extends StdDeserializer<Date> {
private static SimpleDateFormat formatter
= new SimpleDateFormat("dd-MM-yyyy hh:mm:ss");
public CustomDateDeserializer() {
this(null);
}
public CustomDateDeserializer(Class<?> vc) {
super(vc);
}
@Override
public Date deserialize(
JsonParser jsonparser, DeserializationContext context)
throws IOException {
String date = jsonparser.getText();
try {
return formatter.parse(date);
} catch (ParseException e) {
throw new RuntimeException(e);
}
}
}
Далее обратная проверка:
@Test
public void whenDeserializingUsingJsonDeserialize_thenCorrect()
throws IOException {
String json
= "{"name":"party","eventDate":"20-12-2014 02:30:00"}";
SimpleDateFormat df
= new SimpleDateFormat("dd-MM-yyyy hh:mm:ss");
EventWithSerializer event = new ObjectMapper()
.readerFor(EventWithSerializer.class)
.readValue(json);
assertEquals(
"20-12-2014 02:30:00", df.format(event.eventDate));
}
3.6. @JsonAlias
@JsonAlias определяет одно или несколько альтернативных имен свойства во время десериализации.
Давайте посмотрим, как эта аннотация работает на быстром примере:
public class AliasBean {
@JsonAlias({ "fName", "f_name" })
private String firstName;
private String lastName;
}
Здесь у нас есть POJO, и мы хотим десериализовать JSON со значениями, такими как fName, f_name и firstName, в переменную firstName POJO.
Ниже приведен тест, чтобы убедиться, что эта аннотация работает должным образом:
@Test
public void whenDeserializingUsingJsonAlias_thenCorrect() throws IOException {
String json = "{\"fName\": \"John\", \"lastName\": \"Green\"}";
AliasBean aliasBean = new ObjectMapper().readerFor(AliasBean.class).readValue(json);
assertEquals("John", aliasBean.getFirstName());
}
4. Аннотации включения свойств Джексона
4.1. @JsonIgnoreProperties
@JsonIgnoreProperties — это аннотация уровня класса, которая помечает свойство или список свойств, которые Джексон будет игнорировать.
Давайте рассмотрим краткий пример игнорирования идентификатора свойства при сериализации:
@JsonIgnoreProperties({ "id" })
public class BeanWithIgnore {
public int id;
public String name;
}
А теперь тест, подтверждающий, что происходит игнорирование:
@Test
public void whenSerializingUsingJsonIgnoreProperties_thenCorrect()
throws JsonProcessingException {
BeanWithIgnore bean = new BeanWithIgnore(1, "My bean");
String result = new ObjectMapper()
.writeValueAsString(bean);
assertThat(result, containsString("My bean"));
assertThat(result, not(containsString("id")));
}
Чтобы игнорировать любые неизвестные свойства во входных данных JSON без исключения, мы можно установить ignoreUnknown=true для аннотации @JsonIgnoreProperties.
4.2. @JsonIgnore
Напротив, аннотация @JsonIgnore используется для отметки свойства, которое будет игнорироваться на уровне поля.
Давайте используем @JsonIgnore, чтобы игнорировать идентификатор свойства из сериализации:
public class BeanWithIgnore {
@JsonIgnore
public int id;
public String name;
}
Затем мы проверим, чтобы убедиться, что идентификатор был успешно проигнорирован:
@Test
public void whenSerializingUsingJsonIgnore_thenCorrect()
throws JsonProcessingException {
BeanWithIgnore bean = new BeanWithIgnore(1, "My bean");
String result = new ObjectMapper()
.writeValueAsString(bean);
assertThat(result, containsString("My bean"));
assertThat(result, not(containsString("id")));
}
4.3. @JsonIgnoreType
@JsonIgnoreType помечает все свойства аннотированного типа как игнорируемые.
Мы можем использовать аннотацию, чтобы пометить все свойства типа Name как игнорируемые:
public class User {
public int id;
public Name name;
@JsonIgnoreType
public static class Name {
public String firstName;
public String lastName;
}
}
Мы также можем проверить правильность игнорирования:
@Test
public void whenSerializingUsingJsonIgnoreType_thenCorrect()
throws JsonProcessingException, ParseException {
User.Name name = new User.Name("John", "Doe");
User user = new User(1, name);
String result = new ObjectMapper()
.writeValueAsString(user);
assertThat(result, containsString("1"));
assertThat(result, not(containsString("name")));
assertThat(result, not(containsString("John")));
}
4.4. @JsonInclude
Мы можем использовать @JsonInclude для исключения свойств с пустыми/нулевыми значениями/значениями по умолчанию.
Давайте рассмотрим пример исключения нулей из сериализации:
@JsonInclude(Include.NON_NULL)
public class MyBean {
public int id;
public String name;
}
Вот полный тест:
public void whenSerializingUsingJsonInclude_thenCorrect()
throws JsonProcessingException {
MyBean bean = new MyBean(1, null);
String result = new ObjectMapper()
.writeValueAsString(bean);
assertThat(result, containsString("1"));
assertThat(result, not(containsString("name")));
}
4.5. @JsonAutoDetect
@JsonAutoDetect может переопределить семантику по умолчанию, какие свойства видны, а какие нет.
Во-первых, давайте посмотрим, как аннотация может быть очень полезной, на простом примере; давайте включим сериализацию частных свойств:
@JsonAutoDetect(fieldVisibility = Visibility.ANY)
public class PrivateBean {
private int id;
private String name;
}
Затем тест:
@Test
public void whenSerializingUsingJsonAutoDetect_thenCorrect()
throws JsonProcessingException {
PrivateBean bean = new PrivateBean(1, "My bean");
String result = new ObjectMapper()
.writeValueAsString(bean);
assertThat(result, containsString("1"));
assertThat(result, containsString("My bean"));
}
5. Аннотации обработки полиморфных типов Джексона
Далее давайте посмотрим на аннотации обработки полиморфных типов Джексона:
-
«@JsonTypeInfo — указывает сведения о том, какую информацию о типе следует включать в сериализацию. @JsonSubTypes — указывает подтипы аннотированного типа. @JsonTypeName — определяет имя логического типа для использования в аннотированном классе. сложный пример и использовать все три — @JsonTypeInfo, @JsonSubTypes и @JsonTypeName — для сериализации/десериализации объекта Zoo:
Когда мы делаем сериализацию:
public class Zoo {
public Animal animal;
@JsonTypeInfo(
use = JsonTypeInfo.Id.NAME,
include = As.PROPERTY,
property = "type")
@JsonSubTypes({
@JsonSubTypes.Type(value = Dog.class, name = "dog"),
@JsonSubTypes.Type(value = Cat.class, name = "cat")
})
public static class Animal {
public String name;
}
@JsonTypeName("dog")
public static class Dog extends Animal {
public double barkVolume;
}
@JsonTypeName("cat")
public static class Cat extends Animal {
boolean likesCream;
public int lives;
}
}
Вот что сериализует экземпляр Zoo с собакой приведет к:
@Test
public void whenSerializingPolymorphic_thenCorrect()
throws JsonProcessingException {
Zoo.Dog dog = new Zoo.Dog("lacy");
Zoo zoo = new Zoo(dog);
String result = new ObjectMapper()
.writeValueAsString(zoo);
assertThat(result, containsString("type"));
assertThat(result, containsString("dog"));
}
Теперь десериализация. Давайте начнем со следующего ввода JSON:
{
"animal": {
"type": "dog",
"name": "lacy",
"barkVolume": 0
}
}
Затем давайте посмотрим, как он преобразуется в экземпляр Zoo:
{
"animal":{
"name":"lacy",
"type":"cat"
}
}
6. Общие аннотации Джексона
@Test
public void whenDeserializingPolymorphic_thenCorrect()
throws IOException {
String json = "{\"animal\":{\"name\":\"lacy\",\"type\":\"cat\"}}";
Zoo zoo = new ObjectMapper()
.readerFor(Zoo.class)
.readValue(json);
assertEquals("lacy", zoo.animal.name);
assertEquals(Zoo.Cat.class, zoo.animal.getClass());
}
Далее давайте обсудим некоторые из более общих аннотаций Джексона.
6.1. @JsonProperty
Мы можем добавить аннотацию @JsonProperty, чтобы указать имя свойства в JSON.
Давайте используем @JsonProperty для сериализации/десериализации имени свойства, когда мы имеем дело с нестандартными геттерами и сеттерами:
Далее наш тест:
public class MyBean {
public int id;
private String name;
@JsonProperty("name")
public void setTheName(String name) {
this.name = name;
}
@JsonProperty("name")
public String getTheName() {
return name;
}
}
6.2. @JsonFormat
@Test
public void whenUsingJsonProperty_thenCorrect()
throws IOException {
MyBean bean = new MyBean(1, "My bean");
String result = new ObjectMapper().writeValueAsString(bean);
assertThat(result, containsString("My bean"));
assertThat(result, containsString("1"));
MyBean resultBean = new ObjectMapper()
.readerFor(MyBean.class)
.readValue(result);
assertEquals("My bean", resultBean.getTheName());
}
Аннотация @JsonFormat указывает формат при сериализации значений даты и времени.
В следующем примере мы используем @JsonFormat для управления форматом свойства eventDate:
Тогда вот тест:
public class EventWithFormat {
public String name;
@JsonFormat(
shape = JsonFormat.Shape.STRING,
pattern = "dd-MM-yyyy hh:mm:ss")
public Date eventDate;
}
6.3. @JsonUnwrapped
@Test
public void whenSerializingUsingJsonFormat_thenCorrect()
throws JsonProcessingException, ParseException {
SimpleDateFormat df = new SimpleDateFormat("dd-MM-yyyy hh:mm:ss");
df.setTimeZone(TimeZone.getTimeZone("UTC"));
String toParse = "20-12-2014 02:30:00";
Date date = df.parse(toParse);
EventWithFormat event = new EventWithFormat("party", date);
String result = new ObjectMapper().writeValueAsString(event);
assertThat(result, containsString(toParse));
}
@JsonUnwrapped определяет значения, которые должны быть развернуты/сведены при сериализации/десериализации.
Давайте посмотрим, как именно это работает; мы будем использовать аннотацию, чтобы развернуть имя свойства:
Теперь давайте сериализуем экземпляр этого класса:
public class UnwrappedUser {
public int id;
@JsonUnwrapped
public Name name;
public static class Name {
public String firstName;
public String lastName;
}
}
Наконец, вот как выглядит вывод — поля статического вложенного класс разворачивается вместе с другим полем:
@Test
public void whenSerializingUsingJsonUnwrapped_thenCorrect()
throws JsonProcessingException, ParseException {
UnwrappedUser.Name name = new UnwrappedUser.Name("John", "Doe");
UnwrappedUser user = new UnwrappedUser(1, name);
String result = new ObjectMapper().writeValueAsString(user);
assertThat(result, containsString("John"));
assertThat(result, not(containsString("name")));
}
6.4. @JsonView
{
"id":1,
"firstName":"John",
"lastName":"Doe"
}
@JsonView указывает представление, в которое свойство будет включено для сериализации/десериализации.
Например, мы будем использовать @JsonView для сериализации экземпляра объекта Item.
Во-первых, давайте начнем с представлений:
Далее объект Item с использованием представлений:
public class Views {
public static class Public {}
public static class Internal extends Public {}
}
Наконец, полный тест:
public class Item {
@JsonView(Views.Public.class)
public int id;
@JsonView(Views.Public.class)
public String itemName;
@JsonView(Views.Internal.class)
public String ownerName;
}
6.5. @JsonManagedReference, @JsonBackReference
@Test
public void whenSerializingUsingJsonView_thenCorrect()
throws JsonProcessingException {
Item item = new Item(2, "book", "John");
String result = new ObjectMapper()
.writerWithView(Views.Public.class)
.writeValueAsString(item);
assertThat(result, containsString("book"));
assertThat(result, containsString("2"));
assertThat(result, not(containsString("John")));
}
Аннотации @JsonManagedReference и @JsonBackReference могут обрабатывать отношения родитель/потомок и обходить циклы.
В следующем примере мы используем @JsonManagedReference и @JsonBackReference для сериализации нашего объекта ItemWithRef:
Наш объект UserWithRef:
public class ItemWithRef {
public int id;
public String itemName;
@JsonManagedReference
public UserWithRef owner;
}
Затем тест:
public class UserWithRef {
public int id;
public String name;
@JsonBackReference
public List<ItemWithRef> userItems;
}
6.6. @JsonIdentityInfo
@Test
public void whenSerializingUsingJacksonReferenceAnnotation_thenCorrect()
throws JsonProcessingException {
UserWithRef user = new UserWithRef(1, "John");
ItemWithRef item = new ItemWithRef(2, "book", user);
user.addItem(item);
String result = new ObjectMapper().writeValueAsString(item);
assertThat(result, containsString("book"));
assertThat(result, containsString("John"));
assertThat(result, not(containsString("userItems")));
}
@JsonIdentityInfo указывает, что идентификатор объекта следует использовать при сериализации/десериализации значений, например, при решении проблем с бесконечной рекурсией.
В следующем примере у нас есть сущность ItemWithIdentity с двунаправленной связью с сущностью UserWithIdentity:
Сущность UserWithIdentity:
@JsonIdentityInfo(
generator = ObjectIdGenerators.PropertyGenerator.class,
property = "id")
public class ItemWithIdentity {
public int id;
public String itemName;
public UserWithIdentity owner;
}
Теперь давайте посмотрим, как решается проблема бесконечной рекурсии: ~ ~~
@JsonIdentityInfo(
generator = ObjectIdGenerators.PropertyGenerator.class,
property = "id")
public class UserWithIdentity {
public int id;
public String name;
public List<ItemWithIdentity> userItems;
}
Вот полный вывод сериализованного элемента и пользователя:
@Test
public void whenSerializingUsingJsonIdentityInfo_thenCorrect()
throws JsonProcessingException {
UserWithIdentity user = new UserWithIdentity(1, "John");
ItemWithIdentity item = new ItemWithIdentity(2, "book", user);
user.addItem(item);
String result = new ObjectMapper().writeValueAsString(item);
assertThat(result, containsString("book"));
assertThat(result, containsString("John"));
assertThat(result, containsString("userItems"));
}
6.7. @JsonFilter
{
"id": 2,
"itemName": "book",
"owner": {
"id": 1,
"name": "John",
"userItems": [
2
]
}
}
Аннотация @JsonFilter указывает фильтр, который следует использовать во время сериализации.
Сначала мы определяем сущность и указываем на фильтр:
Теперь в полном тесте мы определяем фильтр, который исключает из сериализации все остальные свойства, кроме имени:
@JsonFilter("myFilter")
public class BeanWithFilter {
public int id;
public String name;
}
7 Пользовательская аннотация Джексона
@Test
public void whenSerializingUsingJsonFilter_thenCorrect()
throws JsonProcessingException {
BeanWithFilter bean = new BeanWithFilter(1, "My bean");
FilterProvider filters
= new SimpleFilterProvider().addFilter(
"myFilter",
SimpleBeanPropertyFilter.filterOutAllExcept("name"));
String result = new ObjectMapper()
.writer(filters)
.writeValueAsString(bean);
assertThat(result, containsString("My bean"));
assertThat(result, not(containsString("id")));
}
Далее давайте посмотрим, как создать пользовательскую аннотацию Джексона. Мы можем использовать аннотацию @JacksonAnnotationsInside:
Теперь, если мы используем новую аннотацию для сущности:
@Retention(RetentionPolicy.RUNTIME)
@JacksonAnnotationsInside
@JsonInclude(Include.NON_NULL)
@JsonPropertyOrder({ "name", "id", "dateCreated" })
public @interface CustomAnnotation {}
Мы можем увидеть, как она объединяет существующие аннотации в простую пользовательскую аннотацию, которую мы можем используйте в качестве сокращения:
@CustomAnnotation
public class BeanWithCustomAnnotation {
public int id;
public String name;
public Date dateCreated;
}
Результат процесса сериализации:
@Test
public void whenSerializingUsingCustomAnnotation_thenCorrect()
throws JsonProcessingException {
BeanWithCustomAnnotation bean
= new BeanWithCustomAnnotation(1, "My bean", null);
String result = new ObjectMapper().writeValueAsString(bean);
assertThat(result, containsString("My bean"));
assertThat(result, containsString("1"));
assertThat(result, not(containsString("dateCreated")));
}
8. Аннотации Jackson MixIn
{
"name":"My bean",
"id":1
}
Далее давайте посмотрим, как использовать аннотации Jackson MixIn.
Например, давайте использовать аннотации MixIn для игнорирования свойств типа User:
public class Item {
public int id;
public String itemName;
public User owner;
}
@JsonIgnoreType
public class MyMixInForIgnoreType {}
Затем давайте посмотрим на это в действии:
@Test
public void whenSerializingUsingMixInAnnotation_thenCorrect()
throws JsonProcessingException {
Item item = new Item(1, "book", null);
String result = new ObjectMapper().writeValueAsString(item);
assertThat(result, containsString("owner"));
ObjectMapper mapper = new ObjectMapper();
mapper.addMixIn(User.class, MyMixInForIgnoreType.class);
result = mapper.writeValueAsString(item);
assertThat(result, not(containsString("owner")));
}
9. Отключите аннотацию Джексона
Наконец, давайте посмотрим, как мы можем отключить все аннотации Джексона. Мы можем сделать это, отключив MapperFeature.USE_ANNOTATIONS, как в следующем примере:
@JsonInclude(Include.NON_NULL)
@JsonPropertyOrder({ "name", "id" })
public class MyBean {
public int id;
public String name;
}
«
@Test
public void whenDisablingAllAnnotations_thenAllDisabled()
throws IOException {
MyBean bean = new MyBean(1, null);
ObjectMapper mapper = new ObjectMapper();
mapper.disable(MapperFeature.USE_ANNOTATIONS);
String result = mapper.writeValueAsString(bean);
assertThat(result, containsString("1"));
assertThat(result, containsString("name"));
}
«Теперь, после отключения аннотаций, они не должны иметь никакого эффекта, и должны применяться значения библиотеки по умолчанию:
{"id":1}
Результат сериализации до отключения аннотаций:
{
"id":1,
"name":null
}
Результат сериализации после отключения аннотаций: ~~ ~
10. Заключение
В этой статье мы рассмотрели аннотации Джексона, лишь коснувшись поверхности той гибкости, которую мы можем получить, используя их правильно.