Използване на незадължително с Джаксън

1. Въведение

В тази статия ще дадем преглед на незадължителния клас и след това ще обясним някои проблеми, които може да срещнем, когато го използваме с Джаксън.

След това ще въведем решение, което ще накара Джаксън да се отнася към Optionals , сякаш са обикновени обекти, които могат да бъдат обезсмислени.

2. Преглед на проблема

Първо, нека да разгледаме какво се случва, когато се опитаме да сериализираме и десериализираме Optionals с Jackson.

2.1. Зависимост на Maven

За да използваме Джаксън, нека се уверим, че използваме най-новата му версия:

 com.fasterxml.jackson.core jackson-core 2.11.1 

2.2. Нашият обект на книгата

След това нека създадем книга с класове , съдържаща едно обикновено и едно Незадължително поле:

public class Book { String title; Optional subTitle; // getters and setters omitted }

Имайте предвид, че Optionals не трябва да се използват като полета и ние правим това, за да илюстрираме проблема.

2.3. Сериализация

Сега, нека създадем екземпляр на книга :

Book book = new Book(); book.setTitle("Oliver Twist"); book.setSubTitle(Optional.of("The Parish Boy's Progress"));

И накрая, нека опитаме да го сериализираме с помощта на Jackson ObjectMapper :

String result = mapper.writeValueAsString(book);

Ще видим, че изходът на полето по избор не съдържа неговата стойност, а вместо това вложен JSON обект с поле, наречено настояще :

{"title":"Oliver Twist","subTitle":{"present":true}}

Въпреки че това може да изглежда странно, всъщност трябва да очакваме.

В този случай isPresent () е публичен получател на класа по избор . Това означава, че ще бъде сериализирано със стойност true или false , в зависимост от това дали е празно или не. Това е поведението на Джексън по подразбиране за сериализация.

Ако се замислим, това, което искаме, е действителната стойност на полето на субтитрите да се сериализира.

2.4. Десериализация

Сега, нека обърнем предишния си пример, този път се опитваме да десериализираме обект в незадължителен. Ще видим, че сега получаваме JsonMappingException:

@Test(expected = JsonMappingException.class) public void givenFieldWithValue_whenDeserializing_thenThrowException String bookJson = "{ \"title\": \"Oliver Twist\", \"subTitle\": \"foo\" }"; Book result = mapper.readValue(bookJson, Book.class); } 

Нека да разгледаме проследяването на стека:

com.fasterxml.jackson.databind.JsonMappingException: Can not construct instance of java.util.Optional: no String-argument constructor/factory method to deserialize from String value ('The Parish Boy's Progress')

Това поведение отново има смисъл. По същество Джаксън се нуждае от конструктор, който може да приеме стойността на субтитрите като аргумент. Това не е така с нашето поле по избор .

3. Решение

Това, което искаме, е Джаксън да третира празен незадължителен като нула и да третира настоящия незадължителен като поле, представляващо неговата стойност.

За щастие този проблем е решен за нас. Джаксън има набор от модули, които се занимават с типове данни JDK 8, включително по избор .

3.1. Зависимост и регистрация на Maven

Първо, нека добавим най-новата версия като зависимост на Maven:

 com.fasterxml.jackson.datatype jackson-datatype-jdk8 2.9.6 

Сега всичко, което трябва да направим, е да регистрираме модула с нашия ObjectMapper :

ObjectMapper mapper = new ObjectMapper(); mapper.registerModule(new Jdk8Module());

3.2. Сериализация

Сега да го тестваме. Ако се опитаме да сериализираме обекта си Book отново, ще видим, че вече има субтитри, за разлика от вложен JSON:

Book book = new Book(); book.setTitle("Oliver Twist"); book.setSubTitle(Optional.of("The Parish Boy's Progress")); String serializedBook = mapper.writeValueAsString(book); assertThat(from(serializedBook).getString("subTitle")) .isEqualTo("The Parish Boy's Progress");

Ако се опитаме да сериализираме празна книга, тя ще се съхранява като нула :

book.setSubTitle(Optional.empty()); String serializedBook = mapper.writeValueAsString(book); assertThat(from(serializedBook).getString("subTitle")).isNull();

3.3. Десериализация

Сега, нека повторим нашите тестове за десериализация. Ако препрочетем нашата книга, ще видим, че вече не получаваме JsonMappingException:

Book newBook = mapper.readValue(result, Book.class); assertThat(newBook.getSubTitle()).isEqualTo(Optional.of("The Parish Boy's Progress"));

И накрая, нека повторим теста отново, този път с null. Ще видим, че още веднъж не получаваме JsonMappingException и всъщност имаме празен незадължителен:

assertThat(newBook.getSubTitle()).isEqualTo(Optional.empty());

4. Заключение

Показахме как да заобиколим този проблем, като използваме модула JDK 8 DataTypes, демонстрирайки как позволява на Джаксън да третира празен незадължителен като нула и настоящ незадължителен като обикновено поле.

Изпълнението на тези примери може да бъде намерено в GitHub; това е проект, базиран на Maven, така че трябва да се изпълнява лесно, както е.