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 = 320;
ObjectMapper mapper = new ObjectMapper();
String jsonBook = mapper.writeValueAsString(book);
System.out.println(jsonBook);
}
}
Після запуску main отримаєш таке виведення:
У ObjectMapper-а є багато додаткових налаштувань. Скористаймося одним із них, щоб JSON-рядок був відформатованим таким чином, щоб його було зручно читати. Після створення об'єкту ObjectMapper виконаємо команду:
Інформація у виведенні залишається такою ж, але додалися відступи та перенесення рядків:
"title" : "Джордж і незламний код",
"author" : "Гокінґ Л., Гокінґ С.,
"pages" : 320
}
Десеріалізація з 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\":320}";
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" : 320,
"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" : 320,
"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" : 320,
"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" : 320,
"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" : 320
}
Зараз можеш просто запам'ятати, як використовувати анотації, а під кінець цього модуля ми будемо знайомитися з ними детальніше і створювати власні анотації.
Серіалізація та десеріалізація в XML
Якщо потрібно серіалізувати в XML-формат, можемо також використовувати налаштування й анотації. Єдиною відмінністю буде реалізація об'єкта mapper:
public static void main(String[] args) throws Exception {
Book book = new Book();
book.title = "Джордж і незламний код";
book.author = "Гокінґ Л., Гокінґ С.";
book.pages = 320;
ObjectMapper mapper = new XmlMapper();
mapper.enable(SerializationFeature.INDENT_OUTPUT);
String xmlBook = mapper.writeValueAsString(book);
System.out.println(xmlBook);
}
Виведення:
<title>Джордж і незламний код</title>
<author>Гокінґ Л., Гокінґ С.</author>
<pages>320</pages>
</Book>
Десеріалізація XML:
public static void main(String[] args) throws Exception {
String xmlString = """
<Book>
<title>Джордж і незламний код</title>
<author>Гокінґ Л., Гокінґ С.</author>
<pages>320</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 = 320;
ObjectMapper mapper = new YAMLMapper();
mapper.enable(SerializationFeature.INDENT_OUTPUT);
String yamlBook = mapper.writeValueAsString(book);
System.out.println(yamlBook);
}
Виведення:
title: "Джордж і незламний код"
author: "Гокінґ Л., Гокінґ С."
pages: 320
Десеріалізація YAML:
public static void main(String[] args) throws Exception {
String yamlString = """
---
title: "Джордж і незламний код"
author: "Гокінґ Л., Гокінґ С."
pages: 320""";
ObjectMapper mapper = new YAMLMapper();
Book book = mapper.readValue(yamlString, Book.class);
System.out.println(book);
}
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ