Serializacja i deserializacja — fundamentalne procesy przekształcania obiektów do formatu umożliwiającego przesyłanie lub przechowywanie. W programowaniu mobilnym mechanizmy te są używane przy każdym żądaniu sieciowym, zapisywaniu stanu i komunikacji międzyprocesowej. Według MDN Web Docs, 2024, JSON pozostaje najpopularniejszym formatem serializacji w sieci i aplikacjach mobilnych, wyprzedzając XML i Protocol Buffers.
Najważniejsze
Serializacja — to proces przekształcania obiektu aplikacji w format nadający się do przesłania przez sieć lub zapisania na dysku. Deserializacja wykonuje odwrotne przekształcenie, odtwarzając obiekt z otrzymanych danych. W programowaniu mobilnym serializacja jest stosowana w żądaniach API, zapisywaniu stanu ekranu, buforowaniu i przesyłaniu danych przez Intent lub Bundle.
Każda interakcja między komponentami aplikacji lub między aplikacją a serwerem wymaga serializacji. REST API przesyłają dane w JSON lub XML, gRPC używa Protocol Buffers, a komunikacja wewnątrzprocesowa na Androidzie stosuje Parcelable. Bez serializacji nie jest możliwwe przesłanie złożonego obiektu przez granicę procesu ani zapisanie go w bazie danych.
// Ogólna zasada serializacji
data class User(
val id: Int,
val name: String,
val email: String
)
// Serializacja: obiekt -> JSON
fun serializeUser(user: User): String {
return """{"id":${user.id},"name":"${user.name}","email":"${user.email}"}"""
}
// Deserializacja: JSON -> obiekt
fun deserializeUser(json: String): User? {
// parsowanie JSON do obiektu
return Gson().fromJson(json, User::class.java)
}
| Format | Rozmiar | Szybkość | Czytelność | Typizacja |
|---|---|---|---|---|
| JSON | średni | wysoka | wysoka | dynamiczna |
| XML | duży | średnia | wysoka | schemat XSD |
| Protocol Buffers | mały | bardzo wysoka | niska | ścisła .proto |
| FlatBuffers | mały | maksymalna | niska | ścisła .fbs |
JSON (JavaScript Object Notation) — lekki format tekstowy oparty na składni obiektów JavaScript. JSON obsługuje ciągi znaków, liczby, wartości logiczne, tablice i zagnieżdżone obiekty, co pokrywa większość scenariuszy przesyłania danych w aplikacjach mobilnych. Format jest niezależny od platformy: każda ekosystem udostępnia wbudowane narzędzia do jego parsowania.
JSON wygrywa dzięki prostocie i uniwersalności. Deweloper nie potrzebuje schematu do podstawowego użycia — struktura jest określana dynamicznie podczas parsowania. Czytelność formatu upraszcza debugowanie i testowanie: odpowiedź serwera można podejrzeć w dowolnym narzędziu deweloperskim. Na obu platformach mobilnych JSON jest przetwarzany natywnie bez podłączania zewnętrznych bibliotek.
// Codable — natywna serializacja JSON w Swift
struct User: Codable {
let id: Int
let name: String
let email: String
}
let jsonString = """{"id":1,"name":"John","email":"john@test.com"}"""
let jsonData = Data(jsonString.utf8)
let decoder = JSONDecoder()
let user = try! decoder.decode(User.self, from: jsonData)
Protocol Buffers (protobuf) — binarny format serializacji opracowany przez Google dla systemów o wysokiej wydajności. W przeciwieństwie do JSON, protobuf wymaga wcześniejszego zdefiniowania schematu w pliku .proto, ale zapewnia znacznie mniejszy rozmiar danych i większą szybkość przetwarzania. Format jest używany w gRPC, Firebase Firestore i wewnętrznych serwisach Google.
Schemat protobuf opisuje wiadomości z typowanymi polami, z których każde ma unikalny numer. Kompilator protoc generuje klasy w docelowym języku, które wykonują serializację i deserializację automatycznie. Protobuf wspiera ewolucję schematu poprzez reguły dodawania i usuwania pól bez utraty kompatybilności wstecznej.
// Definicja schematu w user.proto
syntax = "proto3";
message User {
int32 id = 1;
string name = 2;
string email = 3;
repeated string roles = 4;
}
// Wygenerowany kod w Kotlin
val user = UserProto.User.newBuilder()
.setId(1)
.setName("John")
.setEmail("john@test.com")
.build()
val bytes: ByteArray = user.toByteArray()
XML (eXtensible Markup Language) — format o ścisłej strukturze, wsparciu dla przestrzeni nazw i walidacji przez schematy XSD. XML przegrywa z JSON pod względem kompaktowości i szybkości parsowania, ale pozostaje popularny w programowaniu na Androida dla plików layoutów, AndroidManifest, zasobów i konfiguracji Gradle. Na iOS XML jest używany w plikach plist i niektórych starszych serwisach.
Android oferuje trzy sposoby parsowania XML: DOM (załadowanie całego dokumentu do pamięci), SAX (zdarzeniowo zorientowane parsowanie strumieniowe) i XmlPullParser (podejście hybrydowe). XmlPullParser — zalecana opcja dla urządzeń mobilnych, ponieważ efektywnie działa z ograniczoną pamięcią i pozwala przetwarzać dokument w miarę czytania.
// XmlPullParser na Androidzie
fun parseUserXml(inputStream: InputStream): User? {
val parser = Xml.newPullParser()
parser.setInput(inputStream, Xml.Encoding.UTF_8.name)
var id: Int? = null
var name: String? = null
var email: String? = null
while (parser.next() != XmlPullParser.END_DOCUMENT) {
if (parser.eventType == XmlPullParser.START_TAG
&& parser.name == "email") {
email = parser.nextText()
}
}
return User(id!!, name!!, email!!)
}
Każda platforma oferuje własne narzędzia do serializacji. Na iOS głównym mechanizmem jest protokół Codable z JSONEncoder i JSONDecoder, a dla Objective-C — NSJSONSerialization. Android używa bibliotek Gson, Moshi i kotlinx.serialization. Do przesyłania danych między procesami Android stosuje Parcelable, a iOS — NSKeyedArchiver. Projekty Kotlin coraz częściej wybierają kotlinx.serialization — rozwiązanie od JetBrains, które wspiera wieloplatformowość i nie polega na refleksji, generując serializatory na etapie kompilacji poprzez plugin kompilatora Kotlin.
Android obsługuje dwa mechanizmy serializacji dla Intent i Bundle. Serializable — standardowy mechanizm Javy używający refleksji, co prowadzi do niskiej wydajności. Parcelable — protokół specyficzny dla Androida, wymagający ręcznej implementacji metod writeToParcel i createFromParcel, ale działający kilkadziesiąt razy szybciej dzięki bezpośredniemu manipulowaniu bajtami.
// Parcelable na Androidzie
@Parcelize
data class UserParcel(
val id: Int,
val name: String,
val email: String
) : Parcelable
// Przekazywanie przez Intent
val intent = Intent(this, DetailActivity::class.java)
intent.putExtra("user", userParcel)
startActivity(intent)
Na iOS do przesyłania danych między kontrolerami używa się Codable z wcześniejszą serializacją do Data, a do komunikacji międzyprocesowej — NSKeyedArchiver, który przekształca obiekty w binarny format Property List. Nowoczesne projekty w SwiftUI preferują kodowanie danych przez JSONEncoder do przesyłania ich między modułami aplikacji.
Nieprawidłowa deserializacja niezaufanych danych może prowadzić do podatności. Ataki przez serializację wykorzystują nadpisane metody readObject w Javie lub niebezpieczne deserializatory zewnętrznych bibliotek. Na Androidzie należy unikać Serializable dla danych z niezaufanych źródeł, używając zamiast tego Parcelable lub ręcznej walidacji wszystkich pól po deserializacji. Na iOS JSONDecoder jest domyślnie ścisły względem typów, ale podczas pracy z JSONSerialization deweloper musi sprawdzać typy przez warunkowe rzutowanie.
Przy wyborze formatu serializacji uwzględnia się wymagania dotyczące wydajności, rozmiaru danych i kompatybilności. Dla REST API i architektury mikrousługowej optymalnym wyborem pozostaje JSON — jest obsługiwany przez wszystkie platformy i języki. Dla systemów o wysokim obciążeniu i aplikacji mobilnych z ograniczonym ruchem preferowane są Protocol Buffers, które zapewniają mniejszy rozmiar i szybszą deserializację. XML jest uzasadniony tylko w scenariuszach konfiguracyjnych i przy integracji z legacy-systemami. W nowoczesnych projektach zyskuje również na popularności FlatBuffers — format binarny bez etapu deserializacji, używany w silnikach gier i aplikacjach o minimalnych opóźnieniach. Każdy format ma swoją niszę, a właściwy wybór bezpośrednio wpływa na szybkość działania aplikacji i ilość zużywanego ruchu.
Często zadawane pytania
Serializacja — to zapakowanie obiektu aplikacji w format, który można wysłać przez sieć lub zapisać w pliku. Wyobraź sobie, że robisz zdjęcie przedmiotu — zdjęcie to serializacja, a odtworzenie przedmiotu na podstawie zdjęcia to deserializacja.
JSON — format tekstowy czytelny dla człowieka i niewymagający schematu. Protocol Buffers — binarny format z obowiązkowym schematem (.proto), znacznie mniejszym rozmiarem i większą szybkością. Protobuf jest wybierany dla systemów o wysokim obciążeniu, JSON — dla uniwersalnej kompatybilności.
XML pozostaje standardem dla plików konfiguracyjnych (AndroidManifest, zasoby layoutów), dokumentów o złożonej strukturze i systemów ze ścisłą walidacją przez XSD. XML jest również stosowany w protokołach SOAP i legacy-systemach, gdzie wymagane jest wsparcie przestrzeni nazw.
Codable — protokół Swift łączący Encodable i Decodable do automatycznej serializacji i deserializacji. Kompilator generuje implementację metod encode(to:) i init(from:) dla wszystkich właściwości struktury lub klasy.
Parcelable działa znacznie szybciej niż Serializable, ponieważ nie używa refleksji i bezpośrednio zapisuje pola w strumieniu bajtów. Na nowoczesnych urządzeniach różnica może osiągać 10-krotne przyspieszenie na korzyść Parcelable.
Podsumowanie
Opracujemy aplikację mobilną pod klucz
IT Sectr tworzy aplikacje na iOS i Androida dla startupów i firm od 2017 roku. Doradzimy Ci i zaproponujemy najlepsze rozwiązanie.
Przeczytaj również