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

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

1. обзор

В этой статье мы подробно рассмотримJackson Annotations.

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

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

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

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

Read more

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

Как использовать Джексона, чтобы преодолеть проблему бесконечной рекурсии в двунаправленных отношениях.

Read more

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

Используйте Джексона, чтобы отобразить пользовательский JSON на любой граф объектов Java с полным контролем над процессом десериализации.

Read more

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

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

2.1. @JsonAnyGetterс

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

Вот простой пример - сущностьExtendableBean имеет свойство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 какfalse для отключения@JsonAnyGetter().. В этом случаеMap будет преобразован как JSON и появится в переменнойproperties после сериализация.

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, чтобы указатьthe order of properties on serialization.

Установим произвольный порядок свойств объекта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 может бытьinstruct Jackson to serialize a property exactly as is.

В следующем примере мы используем@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 используется - если оболочка включена - для указания имени используемой корневой оболочки.

Перенос означает, что вместо сериализацииUser в нечто вроде:

{
    "id": 1,
    "name": "John"
}

Это будет выглядеть так:

{
    "User": {
        "id": 1,
        "name": "John"
    }
}

Итак, давайте посмотрим на пример -we’re going to use the @JsonRootName annotation to indicate the name of this potential wrapper entity:

@JsonRootName(value = "user")
public class UserWithRoot {
    public int id;
    public String name;
}

По умолчанию именем оболочки будет имя класса -UserWithRoot. Используя аннотацию, мы получаем более чистый на видuser:

@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, новый необязательный аргументnamespace доступен для использования с такими форматами данных, как XML. Если мы добавим его, он станет частью полностью определенного имени:

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

    // ...
}

Если мы сериализуем его с помощьюXmlMapper, результат будет:


    1
    John
    

2.7. @JsonSerializeс

@JsonSerialize indicates a custom serializer to use when сортировкаthe entity.

Давайте посмотрим на небольшой пример. Мы собираемся использовать@JsonSerialize для сериализации свойстваeventDate с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"
}

Однако в нашей целевой сущности нет поляtheName - есть только полеname. Теперь мы не хотим изменять саму сущность - нам просто нужно немного больше контроля над процессом демаршаллинга - путем аннотирования конструктора с помощью@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, ноthe target entity class doesn’t exactly match that data, и поэтому нам нужно настроить процесс, чтобы он подходил.

В следующем примере мы укажем метод 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 {

    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 определяетone or more alternative names for a property during deserialization.

Давайте посмотрим, как работает эта аннотация, на небольшом примере:

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 для@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 может переопределить семантику по умолчаниюwhich properties are visible and which are not.

Давайте посмотрим, как аннотация может быть очень полезной, на простом примере - давайте включим сериализацию частных свойств:

@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с

Мы можем добавитьthe @JsonProperty annotation to indicate the property name in 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 для управления форматом свойстваeventDate:

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 для сериализации экземпляра объектаItem.

Начнем с просмотров:

public class Views {
    public static class Public {}
    public static class Internal extends Public {}
}

А теперь сущностьItem с использованием представлений:

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с

The @JsonManagedReference and @JsonBackReference annotations can handle parent/child relationships и обойти петли.

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

public class ItemWithRef {
    public int id;
    public String itemName;

    @JsonManagedReference
    public UserWithRef owner;
}

Наша сущностьUserWithRef:

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 указывает, что идентификатор объекта следует использовать при сериализации / десериализации значений - например, для решения проблем типа бесконечной рекурсии.

В следующем примере у нас есть сущностьItemWithIdentity с двунаправленными отношениямиa с сущностьюUserWithIdentity:

@JsonIdentityInfo(
  generator = ObjectIdGenerators.PropertyGenerator.class,
  property = "id")
public class ItemWithIdentity {
    public int id;
    public String itemName;
    public UserWithIdentity owner;
}

И объектUserWithIdentity:

@JsonIdentityInfo(
  generator = ObjectIdGenerators.PropertyGenerator.class,
  property = "id")
public class UserWithIdentity {
    public int id;
    public String name;
    public List userItems;
}

Теперьlet’s see how the infinite recursion problem is handled:

@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. Пользовательская аннотация Джексона

Затем давайте посмотрим, как создать собственную аннотацию Джексона. We can make use of the @JacksonAnnotationsInside annotation:с

@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 Аннотации

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

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

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

Реализация всех этих примеров и фрагментов кода можно найти вGitHub project - это проект на основе Maven, поэтому его должно быть легко импортировать и запускать как есть.