Триває набір нової групи на курс Enterprise Patterns! Старт курсу 02.12.2024. Реєструйтеся зі знижкою 30% до 31.10.2024!
Дізнатися більше
11.10.2024
10 хвилин читання

Як парсити JSON у Java?

JSON (JavaScript Object Notation) — це текстовий формат обміну даними, який є простим, зрозумілим і легким для читання як людиною, так і машиною. Він базується на підмножині мови JavaScript, але є незалежним від мови програмування, що робить його ідеальним для передачі даних між сервером і клієнтом у веб-додатках. Завдяки своїй простоті, JSON став надзвичайно популярним форматом для зберігання та передачі даних в інтернеті, замінивши XML.

У сучасних Java-додатках JSON широко використовується для серіалізації та десеріалізації даних, тобто для перетворення об’єктів у формат JSON і навпаки. Це дозволяє легко інтегрувати Java-додатки з іншими сервісами, обмінюватися даними між різними компонентами системи, а також зберігати дані у файлах чи базах даних у зручному та компактному форматі. JSON став стандартом де-факто для API та веб-сервісів, що робить його незамінним інструментом для будь-якого Java-розробника

Для роботи з JSON у Java існує кілька популярних бібліотек, кожна з яких має свої особливості, переваги та недоліки. Найвідомішими з них є Jackson, Gson та JSON-B які ми зараз і розглянемо. Але спочатку рекомендація щодо навчання на курсах програмування. Якщо хочеш стати профі в сфері розробки – тобі у FoxmindEd. Академія, яка реально навчає.

Бажаєте освоїти професію Java Developer? Приєднуйтесь до програми “Від 0 до Strong Java Junior за 12 місяців”. Скористайтесь вигідною пропозицією від 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, що робить його зручним для використання у великих проектах з чіткими стандартами.

Для роботи з 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”, можуть змінюватися або доповнюватися іншими даними в різних сценаріях. Тому не вийде просто створити статичні поля класу для кожного можливого ключа. 

Підпишіться на наш Ютуб-канал! Корисні відео для програмістів чекають на вас! YouTube
Оберіть свій курс програмування! Шлях до кар’єри програміста починається тут! Подивитись

Що ж, перш за все, давайте оновимо наш клас 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.

FAQ
Як парсити JSON у Java?

Для парсингу JSON у Java використовують популярні бібліотеки, такі як Jackson, Gson, або JSON-B. Вони дозволяють легко перетворювати JSON у Java-об'єкти і навпаки.

Що таке ObjectMapper у Jackson?

ObjectMapper — це клас бібліотеки Jackson, який відповідає за перетворення JSON у Java-об'єкти і зворотно.

Як уникнути помилки при парсингу JSON з невідомими полями?

У Jackson можна використовувати метод configure або анотацію @JsonIgnoreProperties.

Чим відрізняється Gson від Jackson?

Gson автоматично ігнорує невідомі поля в JSON, тоді як Jackson за замовчуванням викидає виняток.

Як парсити JSON у Java з використанням JSON-B?

Для цього використовується клас Jsonb, який дозволяє серіалізувати та десеріалізувати об'єкти з JSON.

Як обробляти вкладені об'єкти або масиви в JSON?

Вкладені об'єкти відображаються окремими класами, а масиви — через колекції, наприклад, List для масивів строк.

🤔 Залишилися запитання про парсинг JSON у Java? - Сміливо задавайте нижче! 💬

Додати коментар

Ваш імейл не буде опубліковано. Обов'язкові поля відзначені *

Зберегти моє ім'я, імейл та адресу сайту у цьому браузері для майбутніх коментарів