Що таке Десеріалізації у програмуванні




Що таке Десеріалізації у програмуванні



Серіалізація в Java: Повний посібник

Серіалізація - Це процес збереження стану об'єкта в послідовність байт.

Десеріалізація - це процес відновлення об'єкта з цих байт.

Java Serialization API надає стандартний механізм для створення об'єктів, що серіалізуються. Процес серіалізації полягає у серіалізації кожного поля об'єкта, але тільки в тому випадку, якщо це поле не має специфікатора static або transient. Поля, позначені ними, не можуть бути предметом серіалізації.

import java.io.Serializable; public class Cat implements Serializable < private String name; public Cat(String name) < this.name = name; >>
import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; public class SerializeCat < private static final String FILE_NAME = "testSer.ser"; public static void main(String[] args) <serialize(); Cat cat = deserialize(); >private static Cat deserialize() < Cat cat = null; try (FileInputStream fis = новий FileInputStream(FILE_NAME); ObjectInputStream ois = новий ObjectInputStream(fis)) < cat = (Cat) ois.readObject(); >catch (IOException | ClassNotFoundException e) < System.out.println(e.getMessage()); >return cat; > private static void serialize() < Cat cat = new Cat("Барсик"); try (FileOutputStream fs = новий FileOutputStream(FILE_NAME); ObjectOutputStream os = новий ObjectOutputStream(fs)) < os.writeObject(cat); >catch (IOException e) < System.out.println(e.getMessage()); >> >

Для того, щоб об'єкт міг бути серіалізований, він повинен реалізувати інтерфейс Serializable. Інтерфейс java.io.Serializable не визначає жодних методів. Його присутність лише визначає, що об'єкти цього класу можна серіалізувати. При спробі серіалізувати об'єкт, який не реалізує цей інтерфейс, буде кинуто java.io.NotSerializableException.

Після того, як об'єкт був серіалізований (перетворений на послідовність байт), його можна відновити, при цьому відновлення можна проводити на будь-якій машині (незалежно від того, де проводилася серіалізація).

При десеріалізації поле зі специфікатором transient отримує значення за умовчанням, що відповідає його типу.

Поле зі специфікатором static не змінюється.

Десеріалізація відбувається так: під об'єкт виділяється пам'ять, після чого його поля заповнюються значеннями з потоку. КОНСТРУКТОР об'єкта при цьому НЕ ВИКЛИКАЄТЬСЯ.

Для роботи з серіалізації в java.io визначено інтерфейси ObjectInput, ObjectOutput та реалізують їх класи ObjectInputStream і ObjectOutputStream відповідно. Для серіалізації об'єкта потрібний вихідний потік OutputStream, який слід передати під час конструювання ObjectOutputStream. Після чого виклик методу writeObject() серіалізувати об'єкт та записати його у вихідний потік.

2. Граф вихідного об'єкта

Об'єкт, що серіалізується, може зберігати посилання на інші об'єкти, які в свою чергу так само можуть зберігати посилання на інші об'єкти. І всі посилання теж мають бути відновлені під час десеріалізації. Важливо, що якщо кілька посилань вказують на той самий об'єкт, то у відновлених об'єктах ці посилання так само вказували на той самий об'єкт. Щоб серіалізований об'єкт не було записано двічі, механізм серіалізації певним чином для себе позначає, що об'єкт вже записаний у граф, і коли в черговий раз потрапить посилання на нього, воно буде вказувати на серіалізований об'єкт. Такий механізм необхідний, щоб мати можливість записувати пов'язані об'єкти, які можуть мати перехресні посилання. У разі необхідно відстежувати, чи був об'єкт вже серіалізований, тобто чи потрібно його записувати чи достатньо вказати посилання нею.

Якщо клас містить в якості полів інші об'єкти, то ці об'єкти так само серіалізуватимуться і тому теж повинні бути серіалізуватися.У свою чергу, серіалізуються повинні бути і всі об'єкти, що містяться в цих об'єктах, що серіалізуються, і так далі. Повний шлях посилань об'єкта по всіх об'єктних посиланнях, що є у нього і у всіх об'єктів, на які у нього є посилання, і так далі - називається графом вихідного об'єкта.

import java.io.Serializable; public class Dog implements Serializable < private Collar theCollar; public Dog(Collar collar) < theCollar = collar; >public Collar getCollar() < return theCollar; >@Override public boolean equals(Object o) < if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; Dog dog = (Dog) o; return theCollar! = null? theCollar.equals(dog.theCollar) : dog.theCollar == null; >@Override public int hashCode() < return theCollar != null ? theCollar.hashCode() : 0; >>
import java.io.Serializable; public class Collar implements Serializable < private int collarSize; public Collar(int size) < collarSize = size; >public int getCollarSize() < return collarSize; >@Override public boolean equals(Object o) < if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; Collar collar = (Collar) o; return getCollarSize() == collar.getCollarSize(); >@Override public int hashCode() < return getCollarSize(); >>
import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; public class SerializeDog < private static final String FILE_NAME = "testSer.ser"; public static void main(String[] args) < Collar collar = new Collar(3); Dog dog1 = новий Dog (collar); Dog dog2 = новий Dog (collar); serialize(dog1, dog2); Dog[] deserializedDogs = deserialize(2); System.out.println(dog1.equals(deserializedDogs[0])); System.out.println(dog2.equals(deserializedDogs[1])); Collar desCollar1 = deserializedDogs[0].getCollar(); Collar desCollar2 = deserializedDogs[1].getCollar(); System.out.println(desCollar1.equals(desCollar2)); >private static void serialize(Dog. dogs) <try (FileOutputStream fs = новий FileOutputStream(FILE_NAME); ObjectOutputStream os = новий ObjectOutputStream(fs)) < for (Dog dog: dogs) <os.writeObject(dog); >> catch (IOException e) < System.out.println(e.getMessage()); >> private static Dog[] deserialize(int dogNumber) < Dog[] dogs = new Dog[dogNumber]; try (FileInputStream fs = новий FileInputStream(FILE_NAME); ObjectInputStream os = новий ObjectInputStream(fs)) < for (int i = 0; i < dogNumber; i++) < dogs[i] = (Dog) os.readObject(); >> catch (IOException | ClassNotFoundException e) < System.out.println(e.getMessage()); >return dogs; > >

3. Ключове слово transient

Якщо з якоїсь причини клас не може реалізувати інтерфейс Serializable, змінна цього класу може бути оголошена як transient.

import java.io.Serializable; public class Bird implements Serializable < private String name; private transient Ring ring; public Bird(String name, Ring ring) < this.name = name; this.ring = ring; >public Ring getRing() < return ring; >>
public class Ring < private int size; public Ring(int size) <this.size = size; >public int getSize() < return size; >>
import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; public class SerializeBird < private static final String FILE_NAME = "testSer.ser"; public static void main(String[] args) < Ring ring = new Ring(3); Bird bird = New Bird ("pigeon", ring); System.out.println("Розмір кільця перед серіалізацією: " + bird.getRing().getSize()); try (FileOutputStream fs = новий FileOutputStream(FILE_NAME); ObjectOutputStream os = новий ObjectOutputStream(fs)) < os.writeObject(bird); >catch (IOException e) < System.out.println(e.getMessage()); >try (FileInputStream fis = new FileInputStream("testSer.ser"); ObjectInputStream ois = new ObjectInputStream(fis)) < bird = (Bird) ois.readObject(); >catch (IOException | ClassNotFoundException e) < System.out.println(e.getMessage()); >System.out.println("Розмір кільця після серіалізації: " + bird.getRing().getSize()); > >

4. Десеріалізація та успадкування

При десеріалізації похідного класу, що успадковується від класу, що не серіалізується, викликається конструктор без параметрів батьківського НЕ серіалізованого класу. І якщо такого конструктора не буде – при десеріалізації виникне помилка java.io.InvalidClassException. Конструктор же дочірнього об'єкта, який ми десеріалізуємо, не викликається.

У процесі десеріалізації поля не серіалізуються класів (батьківських класів, що не реалізують інтерфейс Serializable) ініціюються викликом конструктора без параметрів. Такий конструктор повинен бути доступний із серіалізованого їх підкласу. Поля серіалізованого класу будуть відновлені з потоку.

import java.util.Objects; public class Insect < private String name; public Insect(String type) < this.name = type; >public Insect() < >public String getName() < return name; >public void setName(String type) < this.name = type; >@Override public boolean equals(Object o) < if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; Insect insect = (Insect) o; return Objects.equals(name, insect.name); >@Override public int hashCode() < return Objects.hash(name); >@Override public String toString() < return "Insect'; > >
import java.io.Serializable; import java.util.Objects; public class Bug extends Insect implements Serializable < private boolean fly; public Bug(String type, boolean fly) < super(type); this.fly = fly; >public boolean isFly() < return fly; >public void setFly(boolean fly) <this.fly = fly; >@Override public boolean equals(Object o) < if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; if (!super.equals(o)) return false; Bug bug = (Bug) o; return fly == bug.fly; >@Override public int hashCode() < return Objects.hash(super.hashCode(), fly); >@Override public String toString() < return "Bug" + super.toString(); > >
import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; public class SerializeInsectExample < private static final String FILE_NAME = "testSer.ser"; public static void main(String[] args) < Bug bug = new Bug("Травневий жук", true); System.out.println("До серіалізації" + bug); try (FileOutputStream fs = новий FileOutputStream(FILE_NAME); ObjectOutputStream os = новий ObjectOutputStream(fs)) < os.writeObject(bug); >catch (IOException e) < System.out.println(e.getMessage()); >try (FileInputStream fis = новий FileInputStream(FILE_NAME); ObjectInputStream ois = новий ObjectInputStream(fis)) < bug = (Bug) ois.readObject(); >catch (IOException | ClassNotFoundException e) < System.out.println(e.getMessage()); >System.out.println("Після серіалізації" + bug); > >

Введення в серіалізацію та десеріалізацію в Java

Серіалізація та десеріалізація є важливими аспектами програмування, особливо у контексті збереження та передачі об'єктів між різними системами. У мові Java серіалізація та десеріалізація дозволяють перетворювати об'єкти на потік байтів і назад, що забезпечує їх збереження, передачу або відновлення.

Детально про серіалізацію в Java

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

Процес серіалізації

Щоб серіалізувати об'єкт Java, об'єкт повинен реалізовувати інтерфейс `Serializable`. Цей інтерфейс не містить жодних методів, але є маркером, що вказує на те, що об'єкт може бути серіалізований. Процес серіалізації виконується з використанням класу ObjectOutputStream, який перетворює об'єкти в потік байтів.

Ось приклад коду, що демонструє процес серіалізації об'єкта:

```java import java.io.FileOutputStream; import java.io.IOException; import java.io.ObjectOutputStream; import java.io.Serializable; public class SerializationExample < public static void main(String[] args) < // Створення об'єкта для серіалізації Person person = new Person("John", 30); try < // Створення потоку виводу для запису файл FileOutputStream fileOut = new FileOutputStream("person.ser"); // Створення об'єктного потоку виведення для серіалізації об'єкта ObjectOutputStream objectOut = ObjectOutputStream(fileOut); // Серіалізація об'єкта objectOut.writeObject(person); // Закриття потоків objectOut.close(); fileOut.close(); System.out.println("Об'єкт успішно серіалізований"); >catch (IOException e) < e.printStackTrace(); >> > class Person implements Serializable < private String name; private int age; public Person(String name, int age) < this.name = name; this.age = age; >> ```

У цьому прикладі створюється об'єкт `Person` і серіалізується у файлі "person.ser". Зверніть увагу, що клас `Person` реалізує інтерфейс `Serializable`.

Не всі типи об'єктів можуть бути серіалізовані. Деякі типи, такі як потоки, сокети або нитки, не серіалізуються через свою природу. Також може виникнути проблема серіалізації, якщо клас об'єкта змінюється після серіалізації, що може призвести до помилок при десеріалізації.

Докладно про десеріалізацію в Java

Десеріалізація в Java - це процес відновлення об'єкта із серіалізованого стану. При десеріалізації серіалізований потік байтів перетворюється на об'єкт із збереженням його стану, полів і значень. Десеріалізація дозволяє відновити об'єкт, збережений у файлі, переданий через мережу або збережений у пам'яті.

Чи готові поринути у світ програмування, але не знаєте, з чого почати? FoxmindEd допоможе вам швидко освоїти основи та підготуватися до менторингу прямо всередині компанії.

Що таке курс Java Start від FoxmindEd?

✔ Надійний старт: Ми надаємо лише необхідні базові знання, уникаючи зайвої інформації.

✔ Швидке навчання: Курс можна пройти лише за 1-2 тижні.

✔ Якість коштує дорожче: Наші курси дорожчі за конкурентів, але це гарант якості.

Для кого підходить курс Java Start?

  • Тим, хто планує приєднатися до менторингу, але не має досвіду Java.
  • Людям із впевненим володінням комп'ютером та англійською на рівні читання.

Процес десеріалізації

Для десеріалізації об'єкта в Java необхідно виконати такі кроки:

  • Створення потоку введення. Для читання серіалізованих даних необхідно створити об'єкт класу ObjectInputStream. Він служить для зчитування потоку байтів і перетворення їх у об'єкти.
  • Читання об'єкта з потоку. Для десеріалізації об'єкта викликається метод readObject() об'єкта ObjectInputStream. Цей метод повертає відновлений об'єкт, який слід привести до відповідного типу.
  • Закриття потоку. Після завершення десеріалізації необхідно закрити потік введення, викликавши метод close() об'єкта ObjectInputStream.

Ось приклад коду, що демонструє процес десеріалізації об'єкта:

```java import java.io.FileInputStream; import java.io.IOException; import java.io.ObjectInputStream; import java.io.Serializable; public class DeserializationExample < public static void main(String[] args) < try < // Створення потоку введення для читання з файлу FileInputStream fileIn = new FileInputStream("person.ser"); // Створення об'єктного потоку введення для десеріалізації об'єкта ObjectInputStream objectIn = ObjectInputStream(fileIn); // Десеріалізація об'єкта Person person = (Person) objectIn.readObject(); // Закриття потоків objectIn.close(); fileIn.close(); System.out.println("Об'єкт успішно десеріалізований"); System.out.println("Ім'я: " + person.getName()); System.out.println("Вік:" + person.getAge()); >catch (IOException | ClassNotFoundException e) < e.printStackTrace(); >> > class Person implements Serializable < private String name; private int age; public Person(String name, int age) < this.name = name; this.age = age; >public String getName() < return name; >public int getAge() < return age; >> ```

У цьому прикладі відбувається читання об'єкта з файлу "person.ser" та його десеріалізація в об'єкт `Person`. Ми можемо отримати доступ до полів об'єкта, щоб перевірити, чи відновлення пройшло успішно.

Обробка винятків

При десеріалізації необхідно враховувати можливі виняткові ситуації, що можуть виникнути. Наприклад, якщо клас об'єкта, який ми намагаємося десеріалізувати, не знайдено в classpath, буде викинуто виняток `ClassNotFoundException`. Також може виникнути виняток `IOException`, якщо виникають проблеми з читанням із потоку або файлом.

Приклади коду: як провести серіалізацію та десеріалізацію

Приклад серіалізації об'єкта в Java:

```java import java.io.*; public class SerializationExample MyClass obj = New MyClass("Hello, World!", 42); try < // Створення файлу для збереження серіалізованого об'єкта FileOutputStream fileOut = New FileOutputStream("object.ser"); ObjectOutputStream out = New ObjectOutputStream(fileOut); // Серіалізація об'єкта out.writeObject(obj); // Закриття потоків out.close(); fileOut.close(); System.out.println("Об'єкт успішно серіалізований"); >catch (IOException e) < e.printStackTrace(); >> > ```

Приклад десеріалізації об'єкта в Java:

```java import java.io.*; public class DeserializationExample < public static void main(String[] args) < // Відновлення об'єкта із серіалізованого стану MyClass obj = null; try < // Читання серіалізованого об'єкта із файлу FileInputStream fileIn = new FileInputStream("object.ser"); ObjectInputStream in = new ObjectInputStream(fileIn); // Десеріалізація об'єкта obj = (MyClass) in.readObject(); // Закриття потоків in.close(); fileIn.close(); System.out.println("Об'єкт успішно десеріалізований"); >catch (IOException | ClassNotFoundException e) < e.printStackTrace(); >// Використання відновленого об'єкта if (obj != null) < System.out.println("Отримане повідомлення: + obj.getMessage()); System.out.println("Отримане число: " + obj.getNumber()); >> > ```

Розгляд важливості серіалізації та десеріалізації у програмуванні

Серіалізація та десеріалізація відіграють важливу роль у програмуванні та мають безліч практичних застосувань. Ось кілька причин, чому розуміння серіалізації та десеріалізації важливе для Java-програміста.

1. Збереження стану об'єктів. Серіалізація дозволяє зберігати стан об'єктів як потоку байтів. Це корисно, коли потрібно зберегти дані у файл або передати їх через мережу для подальшого використання або відновлення. Наприклад, серіалізація може бути використана для збереження прогресу гри, збереження стану сесії у веб-застосунку або збереження даних на диск.

2. Взаємодія між програмами. Серіалізація та десеріалізація дозволяють передавати об'єкти між різними програмами та системами. Це особливо корисно у розподілених системах, де об'єкти можуть бути передані через мережу для обміну даними між різними компонентами програми. Наприклад, серіалізація може використовуватися для передачі об'єктів між клієнтом та сервером у клієнт-серверних додатках.

3. Реалізація віддаленої взаємодії. Серіалізація дозволяє реалізовувати віддалену взаємодію між віддаленими об'єктами. Це дозволяє викликати методи віддалених об'єктів та передавати дані між клієнтом та сервером, необхідні для виконання віддалених операцій. Наприклад, серіалізація використовується в Java RMI (Remote Method Invocation) для передачі параметрів методів і значень, що повертаються між віддаленими об'єктами.

4. Кешування даних. Серіалізація може бути використана для кешування даних. Коли об'єкт серіалізується, його стан зберігається у вигляді потоку байтів, який можна зберегти в кеші. При необхідності об'єкт може бути відновлений з кешу десеріалізацією, що може значно покращити продуктивність програми. Наприклад, серіалізація може використовуватися для кешування результатів обчислювально-складних операцій або запитів до бази даних.

5. Реалізація механізмів збереження та відновлення. Серіалізація може бути використана для реалізації механізмів збереження та відновлення додатків. Наприклад, програма може серіалізувати стан перед закриттям та зберігати його у файл. Під час наступного запуску програма може десеріалізувати збережений стан та відновити його, що дозволяє користувачам продовжити роботу з місця, де вони зупинилися.

📢 Підпишись на наш Ютуб-канал! 💡Корисні відео для програмістів вже чекають на тебе!

🔍 Вибери свій курс програмування! 🚀 Шлях до кар'єри програміста починається тут!

Механізми серіалізації в Java та Kotlin

У цій статті Ілля Гершман, провідний розробник Юзтех, розглядає поняття серіалізації та десеріалізації порівняно між двома мовами програмування – Java та Kotlin.

Трохи про визначення “серіалізація” та “десеріалізація”

Існує кілька прикладів використання цих механізмів:

  • Зберігання об'єктів у якомусь сховищі. У цьому випадку ми серіалізуємо об'єкт у масив байт, записуємо його в сховище, а потім, через якийсь час, коли нам цей об'єкт знадобиться, ми десеріалізуємо його з масиву байт, отриманого зі сховища.
  • Передача об'єкта між програмами. У цьому випадку один наш додаток серіалізує об'єкт, передає отриманий масив байт якимось чином іншому нашому додатку, а десеріалізацією вже займається останнє.
  • Отримання об'єктного подання запиту чи формування відповіді. У цьому випадку наша програма знаходиться лише на одній стороні і нам, відповідно, потрібно десеріалізувати запит, або серіалізувати відповідь.
  • Внутрішній формат Цей формат розуміє лише та реалізація, яка його й зробила. Java Serializable – наочний приклад реалізації такого формату.
  • XML. Досить широкий формат. На його основі існує безліч підформатів, які реалізуються різними бібліотеками.
  • JSON. Найбільш популярний формат, тому що підтримується різними мовами програмування та має практично однозначний варіант перетворення об'єкта в нього.
  • Avro. Двійковий формат, який підтримується багатьма мовами програмування.
  • Protobuf. Ще один бінарний формат, який підтримується багатьма мовами програмування.

p align="justify"> Важливою властивістю механізму є стійкість до еволюції об'єкта. Це означає, що нам потрібно десеріалізувати об'єкт, який був серіалізований попередньою версією нашої програми (записали у файл об'єкт, потім оновили програму, прочитали об'єкт з файлу).Або навпаки: потрібно, щоб стара версія нашої програми могла десеріалізувати дані, отримані новою версією (оновили лише одну частину нашої програми, і тепер вона надсилає дані у новому форматі, які читає стара версія програми).

Механізми серіалізації по-різному забезпечують цю властивість. Давайте розглянемо кілька варіантів.

Стандартний

Java має стандартний спосіб серіалізації. Його мінус у тому, що прочитати дані можна лише з Java, а в classpath у нас мають бути класи, які ми серіалізували.

import java.io.Serializable; public class Address implements Serializable < private final int countryCode; private final String city; private final String street; public Address(int countryCode, String city, String street) < this.countryCode = countryCode; this.city = city; this.street = street; >@Override public String toString() < return "[Address " + "countryCode=" + countryCode + ", city='" + city + '\'' + ", street='" + street + '\'' + ']'; >>
import java.io.Serializable; public class Person implements Serializable < private final String firstName; private final String lastName; private final Address address; public Person(String firstName, String lastName, Address address) < this.firstName = firstName; this.lastName = lastName; this.address = address; >@Override public String toString() < return "[Person " + "firstName='" + firstName + '\'' + ", lastName='" + lastName + '\'' + ", address java">import java .io.ObjectInputStream; import java.io.ObjectOutputStream; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; public class Main < public static void main(String[] args) throws Throwable < Path path = Paths.get("vasya.dat"); try (ObjectOutputStream oos = New ObjectOutputStream( Files.newOutputStream(path))) < Person person = New Person("Вася", "Пупкін", New Address(7, "Н", "Басейна")); oos.writeObject(person); >try (ObjectInputStream ois = new ObjectInputStream( Files.newInputStream(path))) < Person read = (Person) ois.readObject(); System.out.printf("Read person: %s", read); >> >

Зверніть увагу, як зручно — не довелося нічого робити додатково. JVM сама за нас записала всі поля об'єктів, а потім їх сама прочитала.

Якщо ми змінимо класи, наприклад, додамо номер будинку на адресу, при читанні старого файлу відбудеться помилка java.io.InvalidClassException. Спробуймо цього уникнути.

Зробимо свої методи запису та читання, будемо записувати версію класу і при читанні визначати, які поля потрібно читати, а які ні. Таким чином, ми можемо знати про всі попередні версії та вміти їх вичитувати різними способами, забезпечуючи зворотну сумісність. Пряму сумісність ми таким чином не реалізуємо — у цьому механізмі це не зовсім тривіальне завдання.

import java.io.IOException;import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; = -4554333115192365232L; private static final int = private int countryCode; this.city = city; this.street = street; this.houseNumber = >private void writeObject(ObjectOutputStream oos) oos.writeUTF(street); oos.writeInt(houseNumber); >private void readObject(ObjectInputStream is) city ​​= ois.readUTF(); street = ois.readUTF(); houseNumber = 0; >else if (ver == 2) < countryCode = ois.readInt(); new IOException("Невідома версія: " + ver); >> @Override public String toString() < return "[Address " + "countryCode=" + countryCode + ", city='" + city + '\'' + ", street='" + street + '\'' + ", houseNumber json">public class Address < private final int countryCode; String city, String street) < this.countryCode = countryCode; this.city = city; ; >public String getStreet() < return street; >@Override public String toString() < return "[Address " + "countryCode=" + countryCode + ", city='" + city + '\'' + ", street='" + street + '\'' + ']';
public class Person < private final String firstName; private final String lastName; private final Address address; public Person(String firstName, String lastName, Address address) < this.firstName = firstName; this.lastName = lastName; this.address = address; >public Person() < >public String getFirstName() < return firstName; >public String getLastName() < return lastName; >public Address getAddress() < return address; >@Override public String toString() < return "[Person " + "firstName='" + firstName + '\'' + ", lastName='" + lastName + '\'' + ", address json">import com .fasterxml.jackson.databind.ObjectMapper; public class Main < public static void main(String[] args) throws Throwable < ObjectMapper om = new ObjectMapper(); Person person = new Person("Вася", "Пупкін", new Address(7, "Н", "Басейна")); String json = om.writeValueAsString(person); Person read = om.readValue(json, Person.class); System.out.printf("Read person: %s\n", read); >>

Отримаємо такий рядок:

А що буде, якщо ми захочемо додати номер будинку? Нічого страшного не станеться: поле просто залишиться тим, яким воно було після виклику конструктора за промовчанням.

А якщо навпаки, додамо в JSON houseNumber, а читатимемо старим кодом? Отримаємо помилку com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException. Щоб її уникнути, можна додати інструкцію на клас Address:

@JsonIgnoreProperties(ignoreUnknown = true) public class Address 

Насправді, в бібліотеці є багато різних налаштувань, за допомогою яких можна зробити практично все, що ви хочете.

Jackson XML

Нічого не мене в класах Person та Address ми з мінімальними змінами (створивши інший ObjectMapper) можемо серіалізувати наш об'єкт у XML:

ObjectMapper om = New XmlMapper();

Отримаємо при цьому такий рядок:

ВасяПупкін
7НБасейна

Jackson Avro

Avro формат створений таким чином, що він працює зі схемою даних.Ми повинні вказати схему при серіалізації об'єкта, а також схему при десеріалізації (при цьому є можливість включати схему в дані, що серіалізуються). Одержувач має дві схеми — схему письменника і свою, і він може вирішити, за якою з них читати.

У бібліотеці є можливість отримати схему прямо з нашого POJO, але ми не будемо скористатися цією можливістю, щоб подивитися, що таке Avro схема.

Давайте опишемо схему вручну. Робиться це у JSON форматі:

Наш main тепер буде виглядати так:

import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.dataformat.avro.AvroMapper; import com.fasterxml.jackson.dataformat.avro.AvroSchema; import org.apache.avro.Schema; import java.io.File; public class Main < public static void main(String[] args) throws Throwable < Schema raw = new Schema.Parser() .setValidate(true) .parse(new File("avro-schema.json")); AvroSchema schema = новий AvroSchema (raw); ObjectMapper om = новий AvroMapper(); Person person = new Person("Вася", "Пупкін", new Address(7, "Н", "Басейна")); byte[] bytes = om.writer(schema).writeValueAsBytes(person); Person read = om.readerFor(Person.class) .with(schema) .readValue(bytes); System.out.printf("Read person: %s\n", read); >>

Jackson Protobuf

Protobuf - формат, який також вимагає попереднього опису схеми даних. На цей раз ми скористаємося генератором з POJO:

import com.fasterxml.jackson.dataformat.protobuf.ProtobufMapper; import com.fasterxml.jackson.dataformat.protobuf.schema.ProtobufSchema; public class Main < public static void main(String[] args) throws Throwable < ProtobufMapper om = new ProtobufMapper(); ProtobufSchema schema = om.generateSchemaFor(Person.class); Person person = new Person("Вася", "Пупкін", new Address(7, "Н", "Басейна")); byte[] bytes = om.writer(schema).writeValueAsBytes(person); Person read = om.readerFor(Person.class) .with(schema) .readValue(bytes); System.out.printf("Read person: %s\n", read); >>

Jackson Smile

Smile – це просто бінарний формат вистави JSON'а. Нам потрібно просто створити відповідний ObjectMapper:

ObjectMapper om = new SmileMapper();

Kryo

Kryo - це бібліотека для серіалізації, яка націлена на швидкість та ефективність.

import com.esotericsoftware.kryo.Kryo; import com.esotericsoftware.kryo.io.Input; import com.esotericsoftware.kryo.io.Output; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; public class Main < public static void main(String[] args) throws Throwable < Kryo kryo = new Kryo(); // Необхідно чи зареєструвати всі використовувані класи, kryo.register(Person.class); kryo.register(Address.class); // або вказати, що ми довіряємо джерелу і можна інстанціювати // будь-які класи kryo.setRegistrationRequired(false); Path path = Paths.get("vasya.dat"); try (Output output = new Output(Files.newOutputStream(path))) < Person person = new Person("Вася", "Пупкін", new Address(7, "Н", "Басейна")); kryo.writeObject(output, person); >try (Input input = new Input(Files.newInputStream(path))) < Person read = kryo.readObject(input, Person.class); System.out.printf("Read person: %s\n", read); >> >

Для забезпечення прямої та зворотної сумісності можна вказати:

kryo.setDefaultSerializer(CompatibleFieldSerializer.class);

Kotlin

А тепер подивимося, що цікавого з приводу серіалізації зробили в Kotlin. Так як Kotlin - це JVM based мову, ми можемо користуватися всіма попередніми бібліотеками для серіалізації. Але Kotlin'а має дуже корисну бібліотеку kotlinx.serialization, яка дозволяє будувати схему на етапі компіляції, а не користуватися Reflection API під час виконання. Це забезпечує швидшу роботу.

Давайте для початку перепишемо наші класи на Kotlin:

import kotlinx.serialization.Serializable @Serializable data class Address( val countryCode: Int, val city: String, val street: String, ) @Serializable data class Person( val firstName: String, val lastName: String, val address: Address, )

JSON

Тепер зробимо серіалізацію в JSON:

import kotlinx.serialization.decodeFromString import kotlinx.serialization.encodeToString import kotlinx.serialization.json.Json fun main() < val json = Json val person = Person("Вася", "Пупкін",Address(7, "Н", "Басейна")) val str = json.encodeToString(person) val read = json.decodeFromString(str) println("Read person: $read") >

Додаючи поле в Address, отримаємо kotlinx.serialization.MissingFieldException. Для цього можна вказати значення за промовчанням для цього поля:

@Serializable data class Address( val countryCode: Int, val city: String, val street: String, val houseNumber: Int = 0, )

Protobuf

У Protobuf серіалізація робиться не складніше:

import kotlinx.serialization.Serializable import kotlinx.serialization.decodeFromByteArray import kotlinx.serialization.encodeToByteArray import kotlinx.serialization.protobuf.ProtoBuf Fun main() 7, "Н", "Басейна")) val bytes = protobuf.encodeToByteArray(person) val read = protobuf.decodeFromByteArray(bytes) println("Read person: $read") >

Що можна сказати наприкінці?

Ми розглянули лише невелику кількість варіантів для серіалізації об'єктів у JVM. Вибір методу залежить багатьох чинників. Вирішіть, що вам потрібно: кросплатформність, підтримка зворотної та/або прямої сумісності, швидкість серіалізації та десеріалізації, а також чи важливий розмір отриманих даних.

У будь-якому випадку стандартний метод серіалізації навряд варто використовувати. Ми його розглянули виключно з пізнавальною метою. Він повільний, досить об'ємний і сумісність там робиться руками.

Якщо ви можете використовувати Kotlin, то я б порадив використати його бібліотеку – це зручне та ефективне рішення.

Ну а якщо ви обмежені чистою Java, то, на мій погляд, бібліотека Jackson – чудовий варіант. Вона досить швидка, має безліч налаштувань, а також легко можете поміняти формат, не переписуючи свій код. Формат можна вибрати під ваше конкретне завдання:

  • JSON – на всі випадки життя, тому що він підтримується всіма мовами та фреймворками, а також через його наочність;
  • Protobuf або Avro – якщо потрібна швидкість та мінімальний розмір (у них є відмінності, але їх обговорення – це справа окремої статті);
  • XML – наприклад, якщо вам потрібно валідувати дані XSD, або ще з якихось причин;
  • Ще якийсь формат.

Схожі статті

  • Що таке бекдор у програмуванні
  • Що таке Ципру
  • Що таке режим Atti для дронів Drones Cameras
  • Що таке Адамове яблуко у чоловіків
  • Що таке добори на міжкімнатні
  • Що таке дека на газонокосарці
  • Що таке код помилки 601
  • Що таке Жилка яловича
  • Недавні статті

  • Як бродить зернова брага
  • Що робити якщо не засмагаєш на сонці чому засмага погано лягає на шкіру або перестає прилипати
  • Як швидко зняти гель лак без апарату
  • Як робиться Каті голови
  • Яка гребінець краще для об'єму
  • Чим роблять м'яку покрівлю
  • Чи можна залишати крем для обличчя на ніч
  • Де знаходиться датчик селектора
  • географія нашої діяльності
    вулиця Драгоманова, 27
    вул. Курчатова 1Б
    вул. Міцкевича 130
    вул. Лабунського, 1
    вул. Макарова-Пржевальського
    вул. Толстого 10
    вул. Грушевського 28
    вул. Перший промінь (Черняхівського)
    напишіть нам

    сообщение успешно отправлено
    x