Рубрики
Без рубрики

Примеры аннотаций Джексона

Ядро Джексона – это в основном набор аннотаций-убедитесь, что вы хорошо их понимаете.

Автор оригинала: Eugen Paraschiv.

1. Обзор

В этом уроке мы глубоко погрузимся в Аннотации Джексона .

Мы увидим, как использовать существующие аннотации, как создавать собственные и, наконец, как их отключить.

Дальнейшее чтение:

Больше аннотаций Джексона

Джексон – Двунаправленные Отношения

Начало работы с пользовательской десериализацией в Джексоне

2. Аннотации сериализации Джексона

Сначала мы рассмотрим аннотации сериализации.

2.1. @JsonAnyGetter

Аннотация @JsonAnyGetter позволяет гибко использовать поле Map в качестве стандартных свойств.

Например, объект Extendable Bean имеет свойство name и набор расширяемых атрибутов в виде пар ключ/значение:

public class ExtendableBean {
    public String name;
    private Map properties;

    @JsonAnyGetter
    public Map 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"));
}

Мы также можем использовать необязательный аргумент enabled as false для отключения @JsonAnyGetter(). В этом случае Map будет преобразован в JSON и появится под переменной properties после сериализации.

2.2. @JsonGetter

То @JsonGetter аннотация является альтернативой @JsonProperty аннотация, которая помечает метод как метод геттера.

В следующем примере мы указываем метод get The Name() в качестве метода getter свойства 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}"));
}

Мы также можем использовать необязательный логический аргумент value , который определяет, активна эта аннотация или нет.

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"
    }
}

Итак, давайте рассмотрим пример. W e собираетесь использовать аннотацию @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"
    }
}

Начиная с версии 2.4, новый необязательный аргумент namespace доступен для использования с форматами данных, такими как XML. Если мы добавим его, он станет частью полного имени:

@JsonRootName(value = "user", namespace="users")
public class UserWithRootNamespace {
    public int id;
    public String name;

    // ...
}

Если мы сериализуем его с помощью Xml-картографа, вывод будет:


    1
    John
    

2.7. @JsonSerialize

@JsonSerialize указывает пользовательский сериализатор, который будет использоваться при сортировке сущности.

Давайте рассмотрим краткий пример. Мы собираемся использовать @JsonSerialize для сериализации свойства eventdata с помощью CustomDateSerializer :

public class EventWithSerializer {
    public String name;

    @JsonSerialize(using = CustomDateSerializer.class)
    public Date eventDate;
}

Вот простой пользовательский сериализатор Джексона:

public class CustomDateSerializer extends StdSerializer {

    private static SimpleDateFormat formatter 
      = new SimpleDateFormat("dd-MM-yyyy hh:mm:ss");

    public CustomDateSerializer() { 
        this(null); 
    } 

    public CustomDateSerializer(Class 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"
}

Однако в нашей целевой сущности нет поля Имя , есть только поле имя . Теперь мы не хотим изменять саму сущность, нам просто нужно немного больше контроля над процессом удаления, аннотируя конструктор с помощью @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 для ввода свойства id :

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 позволяет нам гибко использовать Map в качестве стандартных свойств. При десериализации свойства из JSON будут просто добавлены на карту.

Во-первых, мы будем использовать @JsonAnySetter для десериализации сущности ExtendableBean :

public class ExtendableBean {
    public String name;
    private Map 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 , но класс целевой сущности не совсем соответствует этим данным , и поэтому нам нужно настроить процесс, чтобы он соответствовал.

В следующем примере мы зададим методы et Name() в качестве задатчика свойства 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 для десериализации свойства event Data с помощью Customdateserializer :

public class EventWithSerializer {
    public String name;

    @JsonDeserialize(using = CustomDateDeserializer.class)
    public Date eventDate;
}

Вот пользовательский десериализатор:

public class CustomDateDeserializer
  extends StdDeserializer {

    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 это аннотация уровня класса, которая отмечает свойство или список свойств, которые Джексон будет игнорировать.

Давайте рассмотрим краткий пример игнорирования свойства id из сериализации:

@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 of @JsonIgnoreProperties аннотацию.

4.2. @JsonIgnore

В отличие от этого, @JsonIgnore аннотация используется для обозначения свойства, которое должно игнорироваться на уровне поля.

Давайте используем @JsonIgnore , чтобы игнорировать свойство id из сериализации:

public class BeanWithIgnore {
    @JsonIgnore
    public int id;

    public String name;
}

Затем мы проверим, чтобы убедиться, что id был успешно проигнорирован:

@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;
    }
}

Когда мы делаем сериализацию:

@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"));
}

Вот к чему приведет сериализация экземпляра Zoo с помощью Dog :

{
    "animal": {
        "type": "dog",
        "name": "lacy",
        "barkVolume": 0
    }
}

Теперь перейдем к десериализации. Давайте начнем со следующего ввода JSON:

{
    "animal":{
        "name":"lacy",
        "type":"cat"
    }
}

Тогда давайте посмотрим, как это будет не привязано к экземпляру Zoo :

@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. Общие примечания Джексона

Далее давайте обсудим некоторые из более общих аннотаций Джексона.

6.1. @JsonProperty

Мы можем добавить аннотацию @JsonProperty , чтобы указать имя свойства в JSON .

Давайте использовать @JsonProperty для сериализации/десериализации свойства name , когда мы имеем дело с нестандартными геттерами и сеттерами:

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;
    }
}

Далее идет наш тест:

@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());
}

6.2. @JsonFormat

То @JsonFormat аннотация задает формат при сериализации значений даты/времени.

В следующем примере мы используем @JsonFormat для управления форматом свойства Дата события :

public class EventWithFormat {
    public String name;

    @JsonFormat(
      shape = JsonFormat.Shape.STRING,
      pattern = "dd-MM-yyyy hh:mm:ss")
    public Date eventDate;
}

Тогда вот тест:

@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));
}

6.3. @JsonUnwrapped

@JsonUnwrapped определяет значения, которые должны быть развернуты/сплющены при сериализации/десериализации.

Давайте посмотрим, как именно это работает; мы будем использовать аннотацию, чтобы развернуть свойство name :

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")));
}

Наконец, вот как выглядит вывод – поля статического вложенного класса разворачиваются вместе с другим полем:

{
    "id":1,
    "firstName":"John",
    "lastName":"Doe"
}

6.4. @JsonView

@JsonView указывает представление, в которое свойство будет включено для сериализации/десериализации.

Например, мы будем использовать @JsonView для сериализации экземпляра Элемент сущность.

Во-первых, давайте начнем с представлений:

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;
}

Наконец, полный тест:

@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")));
}

6.5. @JsonManagedReference, @JsonBackReference

Аннотации @JsonManagedReference и @JsonBackReference могут обрабатывать отношения родитель/потомок и обходить циклы.

В следующем примере мы используем @JsonManagedReference и @JsonBackReference для сериализации нашего Элемент С Ref сущностью:

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 userItems;
}

Затем тест:

@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")));
}

6.6. @JsonIdentityInfo

@JsonIdentityInfo указывает, что идентификатор объекта следует использовать при сериализации/десериализации значений, например, при работе с бесконечными типами рекурсии, например.

В следующем примере у нас есть Элемент с идентификатором сущность с двунаправленной связью с 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 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"));
}

Вот полный вывод сериализованного элемента и пользователя:

{
    "id": 2,
    "itemName": "book",
    "owner": {
        "id": 1,
        "name": "John",
        "userItems": [
            2
        ]
    }
}

6.7. @JsonFilter

То @JsonFilter аннотация указывает фильтр, который будет использоваться во время сериализации.

Сначала мы определяем сущность и указываем на фильтр:

@JsonFilter("myFilter")
public class BeanWithFilter {
    public int id;
    public String name;
}

Теперь в полном тесте мы определяем фильтр, который исключает из сериализации все другие свойства, кроме name :

@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")));
}

7. Пользовательские аннотации Джексона

Далее давайте посмотрим, как создать пользовательскую аннотацию Джексона. Мы можем использовать @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")));
}

Результат процесса сериализации:

{
    "name":"My bean",
    "id":1
}

8. Примечания Джексона Миксина

Далее давайте посмотрим, как использовать аннотации Джексона Миксина.

Например, давайте использовать аннотации 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. Отключите аннотацию Джексона

Наконец, давайте посмотрим, как мы можем отключить все аннотации Джексона . Мы можем сделать это, отключив функцию Mapper. 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. Заключение

В этой статье мы рассмотрели аннотации Джексона, просто царапая поверхность той гибкости, которую мы можем получить, правильно используя их.

Реализацию всех этих примеров и фрагментов кода можно найти на GitHub .