Десеріалізація: що це, процес відновлення даних

Автор: IT Sectr Опубліковано: 2026-03-08 Час читання: 9 хв

Десеріалізація — процес відновлення об’єкта з потоку даних JSON, XML або Protobuf, необхідний для будь-якого мобільного застосунку, що працює з віддаленим API. За даними Apple Developer (2026), неправильне оброблення вхідних даних залишається однією з частих причин падінь на пристроях. JSONDecoder на iOS і Gson на Android — стандартні інструменти, але кожен має свої особливості та обмеження.

Головне

  • Десеріалізація — відновлення типізованого об’єкта з JSON, XML або Protobuf для використання в коді.
  • Codable — протокол Apple для автоматичної десеріалізації в Swift із підтримкою code generation.
  • Moshi — бібліотека Android від Square із опціями codegen і reflection для різних сценаріїв.
  • Type mismatch — найчастіша помилка при невідповідності типів полів JSON і властивостей моделі.
  • kotlinx.serialization — офіційне рішення JetBrains із компіляторною генерацією безпечного коду.

Що таке десеріалізація?

Десеріалізація — процес перетворення потоку байтів або структурованого тексту в об’єкт мови програмування. У мобільній розробці цей процес відбувається щоразу, коли застосунок отримує відповідь від сервера: рядок JSON перетворюється на екземпляр класу User, Order або Product. Від коректності десеріалізації безпосередньо залежить стабільність екранів, що відображають дані користувачеві.

Відмінність від серіалізації

Серіалізація та десеріалізація — взаємно обернені процеси, рідко симетричні на практиці. Серіалізація перетворює об’єкт у рядок для відправлення на сервер, десеріалізація відновлює об’єкт з отриманого рядка. Сервер може надіслати поле, якого немає в моделі клієнта, використати інший формат дати або повернути null замість числа. За даними Square Engineering (2025), асиметрія форматів є причиною 23% помилок мережевого шару в Android-застосунках. Для зниження ризику застосовують версіонування схеми та строгу контрактну специфікацію через OpenAPI.

Формати даних для десеріалізації

JSON залишається найпопулярнішим форматом для мобільних API завдяки людській читабельності та вбудованій підтримці. Protobuf від Google використовується в high-load системах — він у 3-6 разів компактніший за JSON і швидше парситься, але потребує генерації коду з .proto-файлів і нечитабельний без інструментів. XML рідше зустрічається в сучасних мобільних застосунках, однак застосовується в SOAP-сервісах корпоративних систем і конфігураційних файлах Android. MessagePack — бінарний формат, схожий на JSON за структурою, але компактніший, популярний у системах реального часу.

Як працює десеріалізація

Процес десеріалізації проходить три стадії. Спочатку токенізація розбиває сирий текст на лексеми: ключі, рядки, числа та роздільники. Потім синтаксичний аналіз перевіряє коректність структури — чи закриті дужки, чи правильний тип лапок, чи відповідає формат специфікації RFC 8259. Фінальна стадія — мапінг на об’єктну модель застосунку, де кожному ключу JSON призначається властивість класу з урахуванням стратегії іменування.

Reflection vs Code generation

У мобільній розробці склалися два підходи до мапінгу. Reflection (Gson, JSONSerialization) аналізує структуру класу в runtime через Java Reflection API або Objective-C runtime — це гнучко і не потребує додаткової конфігурації, але повільніше і споживає більше пам’яті. Code generation (Moshi codegen, kotlinx.serialization, Codable) генерує код на етапі компіляції: швидше, безпечніше за типами і не розкриває внутрішню структуру через reflection. JetBrains і Square рекомендують code generation для production-збірок — приріст продуктивності сягає 2-4 разів у бенчмарках Google.

swift
struct User: Codable {
    let id: Int
    let name: String
    let email: String
    let createdAt: Date
}

let json = """
{
    "id": 42,
    "name": "Alice",
    "email": "alice@example.com",
    "created_at": "2026-06-01T12:00:00Z"
}
"""
let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromSnakeCase
let user = try decoder.decode(User.self, from: data)

Приклад десеріалізації JSON у модель User на Swift. Стратегія convertFromSnakeCase автоматично перетворює snake_case ключі API в camelCase властивості моделі — стандартна практика в iOS-проєктах. Параметр data — це сирі байти відповіді сервера, отримані через URLSession. Обробка помилок через try дозволяє перехопити некоректний JSON без крашу застосунку.

Роль стратегій декодування

JSONDecoder підтримує чотири стратегії ключів: useDefaultKeys (точний збіг), convertFromSnakeCase (snake_case → camelCase), custom (closure) і convertFromKebabCase (kebab-case → camelCase). Для дат передбачені .iso8601, .secondsSince1970, .millisecondsSince1970 і кастомний dateFormatter. Вибір правильної стратегії — перший крок до стійкої десеріалізації, що запобігає більшості помилок невідповідності форматів.

Десеріалізація на iOS

JSONDecoder — стандартний механізм десеріалізації в iOS SDK, що працює з протоколом Codable. JSONDecoder автоматично парсить JSON в екземпляри struct або class, підтримуючи вкладені об’єкти, масиви та примітиви. Для кастомної логіки використовується метод init(from: Decoder) — він дозволяє обробити нестандартні формати, пропущені поля на старій версії API або об’єднати кілька ключів JSON в одну властивість.

swift
struct Order: Decodable {
    let orderId: String
    let amount: Double
    let status: OrderStatus

    enum OrderStatus: String, Decodable {
        case pending, confirmed, shipped, cancelled
    }
}

let decoder = JSONDecoder()
decoder.dateDecodingStrategy = .iso8601
let order = try decoder.decode(Order.self, from: jsonData)

DateDecodingStrategy визначає, як JSONDecoder інтерпретує рядки з датами. Найчастіше використовується .iso8601 — стандартний формат REST API. Вкладений enum OrderStatus автоматично декодується з рядкових значень JSON. Це дозволяє уникнути магічних чисел і робить код самодокументованим — статус замовлення завжди має строго визначений набір значень.

Property Wrappers у Codable

Починаючи з Swift 4.2, Codable підтримує property wrappers для кастомної десеріалізації окремих властивостей. @DefaultValue — популярний wrapper, що задає значення за замовчуванням, якщо поле відсутнє в JSON. @LosslessString перетворює рядок у число і навпаки. Це особливо корисно, коли сервер надсилає id у вигляді рядка "123", а модель очікує Int. Property wrappers скорочують шаблонний код в init(from:) і роблять моделі чистішими.

Десеріалізація на Android

На Android вибір бібліотеки десеріалізації залежить від мови та вимог проєкту. Gson від Google — найпоширеніший варіант, що працює через reflection, але має проблеми з продуктивністю на складних ієрархіях. Moshi від Square підтримує як reflection, так і code generation, споживаючи менше пам’яті та швидше обробляючи великі відповіді. kotlinx.serialization від JetBrains — нативне Kotlin-рішення з інтеграцією в компілятор, що не використовує reflection взагалі.

kotlin
@Serializable
data class User(
    @SerialName("user_id")
    val userId: Int,
    val name: String,
    val email: String,
    @SerialName("created_at")
    val createdAt: String
)

val json = Json { ignoreUnknownKeys = true }
val user = json.decodeFromString<User>(response)

@Serializable — анотація Kotlin-компілятора, що активує кодогенерацію для класу. Параметр ignoreUnknownKeys запобігає крашу, якщо сервер надіслав поле, відсутнє в моделі. Для мапінгу snake_case ключів використовується @SerialName — аналог convertFromSnakeCase з iOS. За даними JetBrains (2026), бібліотека підтримує мультиплатформенність: один і той самий клас Serializable працює на Android, iOS (KMP) і серверному Kotlin.

Порівняння Gson, Moshi та kotlinx.serialization

Вибір між бібліотеками зводиться до компромісу швидкість-гнучкість. Gson хороший для прототипів і проєктів на Java — він не потребує анотацій і працює «з коробки». Moshi займає середню позицію: codegen через @JsonClass(generateAdapter = true) дає швидкість, близьку до kotlinx.serialization, а reflection-режим — гнучкість Gson. kotlinx.serialization — найшвидший варіант для чистих Kotlin-проєктів, але потребує Kotlin 1.4+ і плагіна Kotlin Serialization у Gradle.

БібліотекаМеханізмШвидкістьKMP
GsonReflectionНизькаНі
MoshiReflection / CodegenСередня / ВисокаНі
kotlinx.serializationCompiler codegenВисокаТак

Типові помилки та їх запобігання

Type mismatch — ситуація, коли JSON містить значення одного типу, а модель очікує інший. Сервер надіслав рядок "42" замість числа або число 1 замість булевого true. На iOS JSONDecoder за замовчуванням викине DecodingError.typeMismatch, на Android Gson спробує перетворити, а Moshi та kotlinx.serialization потребують явних адаптерів. Рішення — використовувати lenient стратегії або кастомні десеріалізатори для конкретних полів.

Відсутні поля та nullable

Коли сервер не включає опціональне поле, код падає з помилкою. Optional поля на Swift і nullable типи в Kotlin вирішують проблему: якщо поле null або відсутнє в JSON, властивість отримує значення nil/null, а застосунок продовжує роботу. Для обов’язкових полів варто перевіряти їх наявність на рівні API-клієнта до десеріалізації. Moshi та kotlinx.serialization за замовчуванням потребують усі поля — nullable-маркування та default-значення знімають це обмеження.

Несумісність версій API

Зміна структури JSON на сервері — часте джерело production-крашів. Стандартна практика — версіонування схеми через поле version у кореневому об’єкті та підтримка 2-3 попередніх версій на клієнті. kotlinx.serialization дозволяє оголосити кілька моделей під різні версії та вибирати потрібну за полем version після первинного парсингу в JsonElement. Додатковий захист — ignoreUnknownKeys для нових полів і default-значення для полів, які можуть бути видалені.

ПомилкаСимптомБібліотека із захистом
Type mismatchDecodingError / виключенняkotlinx — coerceInputValues = true
Відсутність поляКраш при зверненніMoshi — @Transient + default
Невірний формат датиПомилка декодингуJSONDecoder — dateDecodingStrategy
Зайві поляІгноруються або крашkotlinx — ignoreUnknownKeys = true
Null у non-null поліКраш у runtimeMoshi — lenient із @Nullable

Логування помилок десеріалізації — обов’язкова практика в production. Обгорніть decode в do/catch, логуйте raw JSON і тип очікуваної моделі в Crashlytics або Sentry. Це дозволить швидко визначити, яке поле якого API зламалося і на якій версії застосунку. Без логування помилка десеріалізації виглядає як загадковий краш без контексту.

Часті запитання

Чим десеріалізація відрізняється від парсингу?

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

Яку бібліотеку десеріалізації вибрати для нового Android-проєкту?

Для проєкту на чистому Kotlin рекомендується kotlinx.serialization — вона інтегрована в компілятор, не використовує reflection і підтримує Kotlin Multiplatform. Для існуючого проєкту на Java — Moshi з code generation. Gson краще залишити для легасі-проєктів, де його заміна потребуватиме значних трудозатрат.

Що робити, якщо сервер надсилає snake_case, а модель на camelCase?

На iOS використовуйте keyDecodingStrategy = .convertFromSnakeCase у JSONDecoder. На Android у kotlinx.serialization використовуйте @SerialName для кожного поля. У Moshi застосовуйте @Json(name="field_name") або глобальний JsonAdapter.Factory. Єдиний стиль на рівні проєкту — best practice, узгоджений у контракті API.

Чому десеріалізація викликає краш у production, але не в розробці?

Найчастіше причина — неочікуваний null від сервера на поле, оголошене як обов’язкове. У розробці сервер повертає повні дані, у production — скорочену відповідь. Рішення: маркувати всі потенційно відсутні поля як nullable (Kotlin) або optional (Swift), використовувати ignoreUnknownKeys та default-значення.

Що швидше — Reflection чи Code generation у десеріалізації?

Code generation (Moshi codegen, kotlinx.serialization, Codable) працює в 2-4 рази швидше за reflection у бенчмарках Google. Крім швидкості, кодогенерація безпечніша за типами, не потребує metadata класів у runtime, і помилки типів відловлюються на етапі компіляції, а не в момент десеріалізації.

Підсумки

  • Десеріалізація — фундаментальний процес мобільної розробки, що відновлює об’єкт із JSON, XML або Protobuf для використання в коді застосунку.
  • iOS використовує JSONDecoder з протоколом Codable, що забезпечує автоматичне перетворення з JSON у модель зі стратегіями ключів і дат.
  • Android пропонує три інструменти: Gson (reflection), Moshi (reflection/codegen) і kotlinx.serialization (компіляторна генерація через @Serializable).
  • Типові помилки — type mismatch, відсутність полів, null у non-null полях і несумісність версій API — запобігаються nullable типами, ignoreUnknownKeys і версіонуванням.
  • Code generation безпечніша і швидша за reflection, тому рекомендується для production-збірок мобільних застосунків.
  • Стратегія мапінгу — keyDecodingStrategy на iOS і @SerialName на Android вирішують проблему невідповідності стилів іменування між сервером і клієнтом.
  • Обов’язково логуйте помилки десеріалізації в Crashlytics або Sentry для швидкої діагностики production-інцидентів.

Ми розробимо мобільний застосунок під ключ

IT Sectr створює застосунки для iOS та Android для стартапів і бізнесу з 2017 року. Ми проконсультуємо вас і запропонуємо найкраще рішення.

Обговорити проект

Читайте також