Jackson — это популярная библиотека для сериализации/десериализации Java-объектов в различные текстовые форматы. Основной функционал для работы с форматом JSON — класс ObjectMapper. А работать с другими форматами помогут его наследники (XmlMapper, YAMLMapper). Благодаря наследованию работа со всеми форматами будет происходить единообразно, через единый интерфейс.
Качаем jar-ники
Перед тем, как изучить примеры, нужно скачать jar-файлы Jackson-а и подключить их к проекту в IntellijIDEA. Рассмотрим, как искать нужные файлы на примере jackson-databind:
Перейди на сайт Maven Repository.
В поисковую строку введи “jackson-databind”, получишь результат:
Нас интересует первый результат поиска, переходим по ссылке.
Иногда может потребоваться конкретная версия библиотеки, чтобы обеспечить совместимость с другими компонентами проекта. Тебе подойдет последняя версия (на момент написания лекции это 2.13.2.2), переходи по ссылке.
На открывшейся странице тебе нужна ссылка “bundle”:
Скачиваем jar-файл по ссылке.
Аналогично можно найти и скачать остальные необходимые jar-ники:
Скачав все необходимые файлы, подключи их к проекту в IntellijIDEA:
Открой настройки проекта (это можно сделать комбинацией Ctrl+Alt+Shift+S).
Перейди в Libraries.
Жми +, затем Java, выбери все скачанные файлы. Должно получиться так:
На этом подготовка закончена, будем пробовать ObjectMapper в деле.
Сериализация в JSON
Сначала сериализуем какой-нибудь объект в JSON:
import com.fasterxml.jackson.databind.ObjectMapper;
class Book {
public String title;
public String author;
public int pages;
}
public class Solution {
public static void main(String[] args) throws Exception {
Book book = new Book();
book.title = "Обитаемый остров";
book.author = "Стругацкий А., Стругацкий Б.";
book.pages = 413;
ObjectMapper mapper = new ObjectMapper();
String jsonBook = mapper.writeValueAsString(book);
System.out.println(jsonBook);
}
}
Запустив main, получишь такой вывод:
У ObjectMapper-а есть много дополнительных настроек. Воспользуемся одной из них, чтобы JSON-строка была удобочитаемо отформатирована. После создания объекта ObjectMapper выполним команду:
Информация в выводе осталась та же, но добавились отступы и переносы строк:
"title" : "Обитаемый остров",
"author" : "Стругацкий А., Стругацкий Б.",
"pages" : 413
}
Десериализация из JSON
Теперь выполним обратное действие: десериализуем строку в объект. Чтобы можно было оценить работу программы, в класс Book добавим метод toString:
@Override
public String toString() {
return "Book{" +
"title='" + title + '\'' +
", author='" + author + '\'' +
", pages=" + pages +
'}';
}
И выполним такой main:
public static void main(String[] args) throws Exception {
String jsonString = "{\"title\":\"Обитаемый остров\",\"author\":\"Стругацкий А., Стругацкий Б.\",\"pages\":413}";
Book book = new ObjectMapper().readValue(jsonString, Book.class);
System.out.println(book);
}
Вывод:
Метод readValue перегружен, у него есть много вариаций, принимающих файл, ссылку, различные потоки чтения и т. д. Для упрощения в нашем примере использован вариант, принимающий JSON в виде строки.
Как уже говорилось выше, ObjectMapper имеет много настроек, рассмотрим некоторые из них.
Игнорируем неизвестные свойства
Рассмотрим ситуацию, когда у JSON-строки есть свойство, которого нет в классе Book:
public static void main(String[] args) throws Exception {
String jsonString = """
{
"title" : "Обитаемый остров",
"author" : "Стругацкий А., Стругацкий Б.",
"pages" : 413,
"unknown property" : 42
}""";
ObjectMapper mapper = new ObjectMapper();
Book book = mapper.readValue(jsonString, Book.class);
System.out.println(book);
}
Попытавшись выполнить этот код, получим UnrecognizedPropertyException. Такое поведение установлено по умолчанию, но мы можем его изменить:
ObjectMapper mapper =
new ObjectMapper().configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
При создании объекта ObjectMapper используем метод configure, чтобы установить нужную настройку в false. Метод configure изменяет объект, у которого его вызвали, и возвращает этот же объект, поэтому его можно вызывать и по-другому:
ObjectMapper mapper = new ObjectMapper();
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
По функциональности такая запись аналогична предыдущей.
Если теперь запустить main, десериализация пройдет успешно, а unknown property будет проигнорировано.
Удобные аннотации
Jackson предоставляет нам ряд аннотаций для всесторонней кастомизации сериализации. Рассмотрим несколько самых полезных:
@JsonIgnore — ставится над элементом, который нужно игнорировать при сериализации/десериализации:
class Book {
public String title;
@JsonIgnore
public String author;
public int pages;
}
В результате при сериализации поле author не попадет в результирующий JSON. При десериализации поле author получит значение по умолчанию (null), даже если в JSON-е было другое значение.
@JsonFormat — позволяет задать формат сериализованных данных. Добавим в класс Book еще одно поле типа Date:
class Book {
public String title;
public String author;
public int pages;
public Date createdDate = new Date();
}
После сериализации получим такой JSON:
"title" : "Обитаемый остров",
"author" : "Стругацкий А., Стругацкий Б.",
"pages" : 413,
"createdDate" : 1649330880788
}
Как видишь, дата сериализировалась в виде числа. Добавим аннотацию и зададим формат:
class Book {
public String title;
public String author;
public int pages;
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd")
public Date createdDate = new Date();
}
Теперь результат сериализации:
"title" : "Обитаемый остров",
"author" : "Стругацкий А., Стругацкий Б.",
"pages" : 413,
"createdDate" : "2022-04-07"
}
@JsonProperty — позволяет изменить имя свойства, в которое будет сериализировано поле. Еще этой аннотацией можно помечать методы, и их возвращаемое значение будет преобразовано в JSON-свойство при сериализации:
class Book {
@JsonProperty("name")
public String title;
public String author;
public int pages;
@JsonProperty("quotedTitle")
public String getQuotedTitle() {
return "«" + title + "»";
}
}
Результат сериализации:
"author" : "Стругацкий А., Стругацкий Б.",
"pages" : 413,
"name" : "Обитаемый остров",
"quotedTitle" : "«Обитаемый остров»"
}
@JsonInclude — с помощью этой аннотации можно указать, в каком случае поле должно сериализироваться. Можно добавлять как отдельным полям, так и всему классу. Сначала попробуем сериализовать объект с неинициализированными полями:
public class Solution {
public static void main(String[] args) throws Exception {
Book book = new Book();
ObjectMapper mapper = new ObjectMapper();
mapper.enable(SerializationFeature.INDENT_OUTPUT);
String jsonBook = mapper.writeValueAsString(book);
System.out.println(jsonBook);
}
}
Результат сериализации:
"title" : null,
"author" : null,
"pages" : 0
}
А если добавить аннотацию:
@JsonInclude(JsonInclude.Include.NON_NULL)
class Book {
public String title;
public String author;
public int pages;
}
То получим результат:
"pages" : 0
}
Теперь поля, имеющие значение null, не сериализовались.
@JsonPropertyOrder — позволяет задать порядок сериализации полей:
@JsonPropertyOrder({"author", "title", "pages"})
class Book {
public String title;
public String author;
public int pages;
}
Результат сериализации:
"author" : "Стругацкий А., Стругацкий Б.",
"title" : "Обитаемый остров",
"pages" : 413
}
Сейчас можешь просто запомнить, как использовать аннотации, а в конце этого модуля будем более детально с ними знакомиться и создавать собственные аннотации.
Сериализация и десериализация в XML
Если нужно сериализовать в XML-формат, можем использовать все те же настройки и аннотации. Единственным отличием будет реализация объекта mapper:
public static void main(String[] args) throws Exception {
Book book = new Book();
book.title = "Обитаемый остров";
book.author = "Стругацкий А., Стругацкий Б.";
book.pages = 413;
ObjectMapper mapper = new XmlMapper();
mapper.enable(SerializationFeature.INDENT_OUTPUT);
String xmlBook = mapper.writeValueAsString(book);
System.out.println(xmlBook);
}
Вывод:
<title>Обитаемый остров</title>
<author>Стругацкий А., Стругацкий Б.</author>
<pages>413</pages>
</Book>
Десериализация XML:
public static void main(String[] args) throws Exception {
String xmlString = """
<Book>
<title>Обитаемый остров</title>
<author>Стругацкий А., Стругацкий Б.</author>
<pages>413</pages>
</Book>""";
ObjectMapper mapper = new XmlMapper();
Book book = mapper.readValue(xmlString, Book.class);
System.out.println(book);
}
Сериализация и десериализация в YAML
Аналогично XML действуем и с YAML:
public static void main(String[] args) throws Exception {
Book book = new Book();
book.title = "Обитаемый остров";
book.author = "Стругацкий А., Стругацкий Б.";
book.pages = 413;
ObjectMapper mapper = new YAMLMapper();
mapper.enable(SerializationFeature.INDENT_OUTPUT);
String yamlBook = mapper.writeValueAsString(book);
System.out.println(yamlBook);
}
Вывод:
title: "Обитаемый остров"
author: "Стругацкий А., Стругацкий Б."
pages: 413
Десериализация YAML:
public static void main(String[] args) throws Exception {
String yamlString = """
---
title: "Обитаемый остров"
author: "Стругацкий А., Стругацкий Б."
pages: 413""";
ObjectMapper mapper = new YAMLMapper();
Book book = mapper.readValue(yamlString, Book.class);
System.out.println(book);
}
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ