1. Обзор
В этом кратком руководстве мы покажем два разных способа десериализации неизменяемых объектов Java с помощью библиотеки обработки Jackson JSON.
2. Почему мы используем неизменяемые объекты?
Неизменяемый объект — это объект, который сохраняет свое состояние неизменным с момента своего создания . Это означает, что независимо от того, какие методы объекта вызывает конечный пользователь, объект ведет себя одинаково .
Неизменяемые объекты пригодятся, когда мы разрабатываем систему, которая должна работать в многопоточной среде , поскольку неизменность обычно гарантирует потокобезопасность.
С другой стороны, неизменяемые объекты полезны, когда нам нужно обрабатывать ввод из внешних источников. Например, это может быть пользовательский ввод или какие-то данные из хранилища. В этом случае может оказаться критически важным сохранить полученные данные и защитить их от случайных или непреднамеренных изменений .
Давайте посмотрим, как мы можем десериализовать неизменяемый объект.
3. Публичный конструктор
Рассмотрим структуру класса Employee .
У него есть два обязательных поля: id
и name
, поэтому мы определяем общедоступный конструктор со всеми аргументами , который имеет набор аргументов, соответствующий набору полей объекта:
public class Employee {
private final long id;
private final String name;
public Employee(long id, String name) {
this.id = id;
this.name = name;
}
// getters
}
Таким образом, у нас будут инициализированы все поля объекта в момент создания. Модификаторы final в объявлении полей не позволят нам изменить их значения в будущем. Чтобы сделать этот объект десериализуемым, нам просто нужно добавить пару аннотаций к этому конструктору:
@JsonCreator(mode = JsonCreator.Mode.PROPERTIES)
public Employee(@JsonProperty("id") long id, @JsonProperty("name") String name) {
this.id = id;
this.name = name;
}
Давайте внимательнее посмотрим на аннотации, которые мы только что добавили.
Прежде всего, @JsonCreator
сообщает десериализатору Jackson использовать назначенный конструктор для десериализации .
Есть два режима, которые можно использовать в качестве параметра для этой аннотации — PROPERTIES
и DELEGATING
.
PROPERTIES лучше
всего подходит, когда мы объявляем конструктор со всеми аргументами, тогда как DELEGATING
может быть полезен для конструкторов с одним аргументом.
После этого нам нужно аннотировать каждый из аргументов конструктора с помощью @JsonProperty
, указав имя соответствующего свойства в качестве значения аннотации. Мы должны быть очень осторожны на этом шаге, так как все имена свойств должны совпадать с теми, которые мы использовали при сериализации.
Давайте взглянем на простой модульный тест, который охватывает десериализацию объекта Employee
:
String json = "{\"name\":\"Frank\",\"id\":5000}";
Employee employee = new ObjectMapper().readValue(json, Employee.class);
assertEquals("Frank", employee.getName());
assertEquals(5000, employee.getId());
4. Частный застройщик и застройщик
Иногда бывает, что объект имеет набор необязательных полей. Давайте рассмотрим другую структуру класса, Person
, которая имеет необязательное поле age
:
public class Person {
private final String name;
private final Integer age;
// getters
}
Когда у нас есть значительное количество таких полей, создание публичного конструктора может стать громоздким . Другими словами, нам нужно будет объявить множество аргументов для конструктора и аннотировать каждый из них аннотациями @JsonProperty
. В результате множество повторяющихся объявлений сделают наш код раздутым и трудным для чтения.
Это тот случай, когда на помощь приходит классический паттерн Builder . Давайте посмотрим, как мы можем использовать его мощь в десериализации. Прежде всего, давайте объявим закрытый конструктор со всеми аргументами и класс Builder
:
private Person(String name, Integer age) {
this.name = name;
this.age = age;
}
static class Builder {
String name;
Integer age;
Builder withName(String name) {
this.name = name;
return this;
}
Builder withAge(Integer age) {
this.age = age;
return this;
}
public Person build() {
return new Person(name, age);
}
}
Чтобы десериализатор Джексона использовал этот Builder
, нам просто нужно добавить две аннотации в наш код. Прежде всего, нам нужно пометить наш класс аннотацией @JsonDeserialize
, передав параметр билдера с полным
доменным именем класса билдера .
После этого нам нужно аннотировать сам класс строителя как @JsonPOJOBuilder
:
@JsonDeserialize(builder = Person.Builder.class)
public class Person {
//...
@JsonPOJOBuilder
static class Builder {
//...
}
}
Обратите внимание, что мы можем настроить имена методов, используемых во время сборки.
Параметр buildMethodName
по умолчанию имеет значение « build»
и обозначает имя метода, который мы вызываем, когда билдер готов сгенерировать новый объект .
Другой параметр, withPrefix
, обозначает префикс, который мы добавляем к методам компоновщика, отвечающим за настройку свойств . Значение по умолчанию для этого параметра — «with»
. Вот почему мы не указали ни один из этих параметров в примере.
Давайте взглянем на простой модульный тест, который охватывает десериализацию объекта Person
:
String json = "{\"name\":\"Frank\",\"age\":50}";
Person person = new ObjectMapper().readValue(json, Person.class);
assertEquals("Frank", person.getName());
assertEquals(50, person.getAge().intValue());
5. Вывод
В этой короткой статье мы увидели, как десериализовать неизменяемые объекты с помощью библиотеки Джексона.
Весь код, относящийся к этой статье, можно найти на GitHub .