JSON (JavaScript Object Notation) — це текстовий формат обміну даними, який є простим, зрозумілим і легким для читання як людиною, так і машиною. Він базується на підмножині мови JavaScript, але є незалежним від мови програмування, що робить його ідеальним для передачі даних між сервером і клієнтом у веб-додатках. Завдяки своїй простоті, JSON став надзвичайно популярним форматом для зберігання та передачі даних в інтернеті, замінивши XML.
У сучасних Java-додатках JSON широко використовується для серіалізації та десеріалізації даних, тобто для перетворення об’єктів у формат JSON і навпаки. Це дозволяє легко інтегрувати Java-додатки з іншими сервісами, обмінюватися даними між різними компонентами системи, а також зберігати дані у файлах чи базах даних у зручному та компактному форматі. JSON став стандартом де-факто для API та веб-сервісів, що робить його незамінним інструментом для будь-якого Java-розробника.
Для роботи з JSON у Java існує кілька популярних бібліотек, кожна з яких має свої особливості, переваги та недоліки. Найвідомішими з них є Jackson, Gson та JSON-B які ми зараз і розглянемо. Але спочатку рекомендація щодо навчання на курсах програмування. Якщо хочеш стати профі в сфері розробки – тобі у FoxmindEd. Академія, яка реально навчає.
Jackson
Jackson надає кілька основних класів та методів для роботи з JSON, серед яких найважливішим є клас ObjectMapper. Цей клас відповідає за перетворення JSON у Java-об’єкти та навпаки.
- ObjectMapper: Основний клас для парсингу JSON. Він забезпечує всі необхідні методи для перетворення між JSON і Java-об’єктами.
- readValue: Метод, який використовується для десеріалізації JSON у Java-об’єкт.
- writeValueAsString: Метод, який використовується для серіалізації Java-об’єкта в JSON.
Розглянемо простий приклад, де ми парсимо JSON у Java-об’єкт та зворотно серіалізуємо цей об’єкт у JSON. Спочатку нам треба створити клас, що буде відображати JSON, який ми хочемо розпарсити.
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
public class User {
private Integer id;
private String name;
private int age;
private String email;
}
Примітка: не забудьте додати до класу User конструктор без параметрів інакше у методі readValue отримаєте помилку – InvalidDefinitionException.
import com.fasterxml.jackson.databind.ObjectMapper;
public class Main {
public static void main(String[] args) {
try {
// JSON рядок
String jsonString = "{\"id\":1,\"name\":\"John Doe\",\"age\":30,\"email\":\"johndoe@example.com\"}";
ObjectMapper objectMapper = new ObjectMapper();
// Парсимо JSON у Java-об'єкт
User user = objectMapper.readValue(jsonString, User.class);
System.out.println("Іd: " + user.getId());
System.out.println("Ім'я: " + user.getName());
System.out.println("Вік: " + user.getAge());
System.out.println("Email: " + user.getEmail());
// Серіалізуємо Java-об'єкт у JSON
String jsonOutput = objectMapper.writeValueAsString(user);
System.out.println("Серіалізований JSON: " + jsonOutput);
} catch (Exception e) {
e.printStackTrace();
}
}
}
Цікаво, що буде, а якщо ми не хочемо десерізувати певні поля JSON? Давайте додамо ще одне поле в JSON, але клас User лишиться без змін.
//Додали 'phone' в JSON
String jsonString = "{\"id\":1,\"name\":\"John Doe\"," +
"\"age\":30,\"email\":\"johndoe@example.com\",\"phone\":\"+1234567890\"}";
Після запуску коду ми отримаємо помилку UnrecognizedPropertyException. Адже поля phone немає в класі User і ObjectMapper не знає, що із цим полем робити.
Щоб цього уникнути можна налаштувати ObjectMapper таким чином, щоб він ігнорував невідомі поля:
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
Або достатньо додати анотацію над класом User:
@JsonIgnoreProperties(ignoreUnknown = true)
Gson
Gson надає прості у використанні методи для серіалізації та десеріалізації даних:
- Gson: Основний клас, який відповідає за серіалізацію та десеріалізацію JSON.
- fromJson: Метод для перетворення JSON у Java-об’єкт. Він приймає JSON-рядок та клас, до якого цей JSON повинен бути перетворений.
- toJson: Метод для перетворення Java-об’єкта у JSON-рядок.
Важливо зазначити, що на відміну від ObjectMapper з бібліотеки Jackson, який за замовчуванням викидає виняток при наявності невідомих полів у JSON, Gson за замовчуванням ігнорує такі поля і не викликає помилок. Це означає, що якщо у JSON присутнє поле, якого немає у відповідному Java-класі, Gson просто пропустить це поле без будь-яких наслідків. Такий підхід робить Gson більш гнучким і стійким до змін структури JSON, але також може призвести до того, що ви не помітите помилок у даних, якщо якісь поля несподівано зміняться або будуть додані.
Клас User лишаємо без змін. Лише зауважу, що для Gson не потрібно додавати до класу дефолтний (пустий) конструктор, як ми робили це із ObjectMapper.
import com.google.gson.Gson;
public class Main {
public static void main(String[] args) {
String jsonString = "{\"id\":1,\"name\":\"John Doe\",\"age\":30,\"email\":\"johndoe@example.com\"}";
Gson gson = new Gson();
User user = gson.fromJson(jsonString, User.class);
System.out.println("Іd: " + user.getId());
System.out.println("Ім'я: " + user.getName());
System.out.println("Вік: " + user.getAge());
System.out.println("Email: " + user.getEmail());
String jsonOutput = gson.toJson(user);
System.out.println("Серіалізований JSON: " + jsonOutput);
}
}
Gson дозволяє налаштовувати процес парсингу за допомогою анотацій. Найпоширеніші анотації:
- @SerializedName: Використовується для вказання альтернативного імені поля в JSON.
- @Expose: Використовується для контролю серіалізації та десеріалізації полів. Наприклад, якщо використовувати @Expose(serialize = false), то поле буде виключено з серіалізації, але залишиться доступним для десеріалізації. Це може бути корисно, коли деякі дані потрібно приховати при серіалізації, але при цьому їх необхідно отримувати з JSON при десеріалізації.
Додамо нові анотації до класу User
import com.google.gson.annotations.Expose;
import com.google.gson.annotations.SerializedName;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
@AllArgsConstructor
public class User {
@SerializedName("user_id")
@Expose
private Integer id;
@Expose(serialize = false)
private String name;
@Expose
private int age;
@Expose
private String email;
}
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
public class Main {
public static void main(String[] args) {
// JSON рядок
String jsonString = "{\"user_id\":1,\"name\":\"John Doe\",\"age\":30,\"email\":\"johndoe@example.com\"}";
// Створення екземпляру Gson з урахуванням @Expose
Gson gson = new GsonBuilder().excludeFieldsWithoutExposeAnnotation().create();
User user = gson.fromJson(jsonString, User.class);
System.out.println("Іd: " + user.getId());
System.out.println("Name: " + user.getName());
System.out.println("Age: " + user.getAge());
System.out.println("Email: " + user.getEmail());
// Серіалізуємо Java-об'єкт у JSON
String jsonOutput = gson.toJson(user);
System.out.println("Серіалізований JSON: " + jsonOutput); // не міститиме поля name
}
}
JSON-B
JSON-B (Java API for JSON Binding) — це офіційний стандарт Java для серіалізації та десеріалізації JSON у Java-об’єкти і навпаки. JSON-B входить до складу Java EE (Enterprise Edition) і є частиною специфікації Jakarta EE, що робить його природним вибором для корпоративних Java-додатків, особливо тих, які використовують Java EE або Jakarta EE. JSON-B надає простий та уніфікований API для роботи з JSON, що робить його зручним для використання у великих проектах з чіткими стандартами.
курси Junior саме для вас.
Для роботи з JSON-B використовується декілька основних класів та методів:
- Jsonb: Це основний інтерфейс, який надає методи для серіалізації та десеріалізації об’єктів.
- JsonbBuilder: Клас для створення екземпляра Jsonb.
- fromJson: Метод для десеріалізації JSON-рядка в Java-об’єкт.
- toJson: Метод для серіалізації Java-об’єкта в JSON-рядок.
import jakarta.json.bind.annotation.JsonbProperty;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
public class User {
@JsonbProperty("user_id")
private Integer id;
private String name;
private int age;
private String email;
}
import jakarta.json.bind.Jsonb;
import jakarta.json.bind.JsonbBuilder;
public class Main {
public static void main(String[] args) {
String jsonString = "{\"user_id\":1,\"name\":\"John Doe\",\"age\":30,\"email\":\"johndoe@example.com\"}";
Jsonb jsonb = JsonbBuilder.create();
User user = jsonb.fromJson(jsonString, User.class);
System.out.println("Id: " + user.getId());
System.out.println("Name: " + user.getName());
System.out.println("Age: " + user.getAge());
System.out.println("Email: " + user.getEmail());
String jsonOutput = jsonb.toJson(user);
System.out.println("Серіалізований JSON: " + jsonOutput);
}
}
Додатково
Обробка складних структур
До цього ми розглядали приклад, де JSON представляв собою простий об’єкт з кількома основними полями. Однак у реальних додатках часто виникає потреба працювати зі складнішими структурами, які містять масиви та вкладені об’єкти. Наш оновлений приклад:
{
"id": 1,
"name": "John Doe",
"age": 30,
"email": "johndoe@example.com",
"phones": ["+1234567890", "+0987654321"],
"address": {
"street": "123 Main St",
"city": "Anytown"
},
"additionalInfo": {
"nickname": "JD",
"hobbies": "coding"
}
}
Примітка: Поле additionalInfo у цьому прикладі представляє собою динамічно змінювані дані. Це означає, що конкретні ключі, такі як “nickname” та “hobbies”, можуть змінюватися або доповнюватися іншими даними в різних сценаріях. Тому не вийде просто створити статичні поля класу для кожного можливого ключа.
Що ж, перш за все, давайте оновимо наш клас User відповідно до нового JSON. Квадратні дужки після phones вказують на наявність масиву, тому для зберігання телефонів ми використаємо тип List<String>. Поле address у JSON є вкладеним об’єктом, який містить інформацію про вулицю та місто. Для його відображення ми створимо окремий клас Address, де кожне поле буде представлене відповідним типом.
Що стосується поля additionalInfo, це особливий випадок. У JSON воно представлено як об’єкт, але його ключі та значення можуть змінюватися динамічно. Оскільки ми не знаємо заздалегідь, які саме ключі можуть бути присутніми, ми використовуємо Map<String, Object>. Це надає нам гнучкість для зберігання довільних пар “ключ-значення”, які можуть змінюватися в залежності від ситуації.
@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
public class User {
private Integer id;
private String name;
private int age;
private String email;
private List<String> phones;
private Address address;
private Map<String, Object> additionalInfo;
@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
public static class Address {
private String street;
private String city;
}
}
Для парсингу використаємо ObjectMapper:
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
// Парсимо JSON у Java-об'єкт
User user = objectMapper.readValue(jsonString, User.class);
// Серіалізуємо Java-об'єкт у JSON
String jsonOutput = objectMapper.writeValueAsString(user);
System.out.println("Серіалізований JSON: " + jsonOutput);
Висновок
Парсинг JSON у Java є важливою частиною розробки сучасних додатків, де обмін даними між компонентами або сервісами здійснюється за допомогою цього популярного формату. Важливо розуміти основні бібліотеки для роботи з JSON, такі як Jackson, Gson та JSON-B, кожна з яких має свої переваги та особливості використання. Вибір бібліотеки залежить від конкретних вимог вашого проекту та зручності їх використання.
Не менш важливим є налаштування поведінки бібліотеки у випадку некоректного формату JSON. Це дозволяє уникнути неочікуваних збоїв і забезпечити більш надійну обробку даних. Використання вбудованих механізмів для обробки помилок або налаштування бібліотеки таким чином, щоб вона ігнорувала невідомі поля, може значно підвищити стійкість додатка.
Підсумовуючи, парсинг JSON у Java — це не просто вибір бібліотеки. Це також розуміння того, як налаштувати її під конкретні вимоги, обробляти помилки та ефективно працювати з великими обсягами даних. Дотримуючись цих принципів, ви зможете створювати надійні та ефективні додатки, які будуть легко справлятися з будь-якими завданнями, пов’язаними з обробкою JSON.
🤔 Залишилися запитання про парсинг JSON у Java? - Сміливо задавайте нижче! 💬