Deserializarea este procesul de recuperare a unui obiect dintr-un flux de date JSON, XML sau Protobuf, necesar pentru orice aplicație mobilă care comunică cu un API la distanță. Conform Apple Developer (2026), procesarea incorectă a datelor primite rămâne una dintre cauzele frecvente ale căderilor pe dispozitive. JSONDecoder pe iOS și Gson pe Android sunt instrumente standard, dar fiecare are caracteristicile și limitările sale.
Principalele aspecte
Deserializarea — procesul de transformare a unui flux de octeți sau a unui text structurat într-un obiect al limbajului de programare. În dezvoltarea mobilă, acest proces are loc de fiecare dată când aplicația primește un răspuns de la server: un șir JSON se transformă într-o instanță a clasei User, Order sau Product. Stabilitatea ecranelor care afișează date utilizatorului depinde direct de corectitudinea deserializării.
Serializarea și deserializarea sunt procese reciproc inverse, rareori simetrice în practică. Serializarea transformă un obiect într-un șir pentru a fi trimis la server, deserializarea recuperează obiectul din șirul primit. Serverul poate trimite un câmp care nu există în modelul clientului, poate folosi un alt format de dată sau poate returna null în locul unui număr. Conform Square Engineering (2025), asimetria formatelor este cauza a 23% dintre erorile stratului de rețeanță în aplicațiile Android. Pentru reducerea riscului, se aplică versionarea schemei și specificații contractuale stricte prin OpenAPI.
JSON rămâne cel mai popular format pentru API-urile mobile datorită lizibilității și suportului integrat. Protobuf de la Google este utilizat în sistemele high-load — este de 3-6 ori mai compact decât JSON și se parsează mai repede, dar necesită generarea de cod din fișiere .proto și este ilizibil fără instrumente. XML este mai rar întâlnit în aplicațiile mobile moderne, dar este utilizat în serviciile SOAP ale sistemelor corporative și fișierele de configurare Android. MessagePack — format binar similar JSON ca structură, dar mai compact, popular în sistemele de timp real.
Procesul de deserializare parcurge trei etape. În primul rând, tokenizarea împarte textul brut în lexeme: chei, șiruri, numere și separatori. Apoi, analiza sintactică verifică corectitudinea structurii — dacă parantezele sunt închise, dacă tipul ghilimelelor este corect, dacă formatul respectă specificația RFC 8259. Etapa finală este maparea pe modelul obiectual al aplicației, unde fiecărei chei JSON i se atribuie o proprietate a clasei ținând cont de strategia de denumire.
În dezvoltarea mobilă s-au format două abordări ale mapării. Reflection (Gson, JSONSerialization) analizează structura clasei în runtime prin Java Reflection API sau Objective-C runtime — este flexibilă și nu necesită configurare suplimentară, dar este mai lentă și consumă mai multă memorie. Code generation (Moshi codegen, kotlinx.serialization, Codable) generează cod în faza de compilare: mai rapidă, mai sigură din punct de vedere al tipurilor și nu expune structura internă prin reflection. JetBrains și Square recomandă code generation pentru versiunile de producție — creșterea performanței atinge de 2-4 ori în benchmark-urile Google.
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)
Exemplu de deserializare JSON în modelul User în Swift. Strategia convertFromSnakeCase transformă automat cheile API în format snake_case în proprietățile modelului în camelCase — practică standard în proiectele iOS. Parametrul data reprezintă octeții bruti ai răspunsului serverului, primiți prin URLSession. Gestionarea erorilor prin try permite captarea JSON-ului incorect fără a provoca căderea aplicației.
JSONDecoder suportă patru strategii de chei: useDefaultKeys (potrivire exactă), convertFromSnakeCase (snake_case → camelCase), custom (closure) și convertFromKebabCase (kebab-case → camelCase). Pentru date sunt prevăzute .iso8601, .secondsSince1970, .millisecondsSince1970 și dateFormatter personalizat. Alegerea strategiei potrivite este primul pas spre o deserializare stabilă, prevenind majoritatea erorilor de nepotrivire a formatelor.
JSONDecoder — mecanismul standard de deserializare în iOS SDK, care funcționează cu protocolul Codable. JSONDecoder parsează automat JSON în instanțe de struct sau class, suportând obiecte imbricate, array-uri și tipuri primitive. Pentru logică personalizată se utilizează metoda init(from: Decoder) — aceasta permite gestionarea formatelor nestandard, a câmpurilor omise într-o versiune mai veche de API sau combinarea mai multor chei JSON într-o singură proprietate.
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 stabilește modul în care JSONDecoder interpretează șirurile cu date. Cel mai frecvent este utilizat .iso8601 — formatul standard al API-urilor REST. Enum-ul imbricat OrderStatus este decodat automat din valorile șirurilor JSON. Acest lucru evită numerele magice și face codul auto-documentat — statusul comenzii are întotdeauna un set strict definit de valori.
începând cu Swift 4.2, Codable suportă property wrappers pentru deserializarea personalizată a proprietăților individuale. @DefaultValue — un wrapper popular care setează o valoare implicită dacă câmpul lipsește din JSON. @LosslessString transformă un șir într-un număr și invers. Acest lucru este util mai ales când serverul trimite id-ul ca șir „123”, iar modelul așteaptă Int. Property wrappers reduc codul standard în init(from:) și fac modelele mai curate.
Pe Android, alegerea bibliotecii de deserializare depinde de limbajul și cerințele proiectului. Gson de la Google — cea mai răspândită opțiune, care funcționează prin reflection, dar are probleme de performanță pe ierarhii complexe. Moshi de la Square suportă atât reflection, cât și code generation, consumând mai puțină memorie și procesând mai rapid răspunsurile mari. kotlinx.serialization de la JetBrains — soluția nativă Kotlin cu integrare în compilator, care nu folosește reflection deloc.
@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 — adnotarea compilatorului Kotlin care activează generarea de cod pentru clasă. Parametrul ignoreUnknownKeys previne căderile dacă serverul a trimis un câmp care nu există în model. Pentru maparea cheilor snake_case se utilizează @SerialName — echivalentul lui convertFromSnakeCase din iOS. Conform JetBrains (2026), biblioteca suportă multi-platformă: aceeași clasă Serializable funcționează pe Android, iOS (KMP) și Kotlin pe server.
Alegerea între biblioteci se reduce la un compromis viteză-flexibilitate. Gson este bun pentru prototipuri și proiecte în Java — nu necesită adnotări și funcționează „din cutie”. Moshi ocupă o poziție intermediară: codegen prin @JsonClass(generateAdapter = true) oferă viteză apropiată de kotlinx.serialization, iar modul reflection — flexibilitatea lui Gson. kotlinx.serialization — cea mai rapidă opțiune pentru proiecte pure Kotlin, dar necesită Kotlin 1.4+ și pluginul Kotlin Serialization în Gradle.
| Bibliotecă | Mecanism | Viteză | KMP |
|---|---|---|---|
| Gson | Reflection | Scăzută | Nu |
| Moshi | Reflection / Codegen | Medie / Ridicată | Nu |
| kotlinx.serialization | Compiler codegen | Ridicată | Da |
Type mismatch — situația în care JSON conține o valoare de un tip, iar modelul așteaptă alt tip. Serverul a trimis șirul „42” în locul unui număr sau numărul 1 în loc de boolean true. Pe iOS, JSONDecoder va arunca implicit DecodingError.typeMismatch, pe Android Gson va încerca să convertească, iar Moshi și kotlinx.serialization necesită adaptoare explicite. Soluția — utilizarea strategiilor lenient sau a deserializatoarelor personalizate pentru câmpuri specifice.
Când serverul nu include un câmp opțional, codul cade cu eroare. Optional în Swift și tipurile nullable în Kotlin rezolvă problema: dacă un câmp este null sau lipsește din JSON, proprietatea primește valoarea nil/null, iar aplicația continuă să funcționeze. Pentru câmpurile obligatorii, merită să verificați prezența lor la nivelul clientului API înainte de deserializare. Moshi și kotlinx.serialization necesită implicit toate câmpurile — marcarea nullable și valorile implicite elimină această restricție.
Modificarea structurii JSON pe server — sursă frecventă de căderi în producție. Practica standard este versionarea schemei prin câmpul version în obiectul rădăcină și suportul pentru 2-3 versiuni anterioare în client. kotlinx.serialization permite declararea mai multor modele pentru versiuni diferite și selectarea celui potrivit pe baza câmpului version după parsarea inițială în JsonElement. Protecție suplimentară — ignoreUnknownKeys pentru câmpuri noi și valori implicite pentru câmpuri care ar putea fi eliminate.
| Eroare | Simptom | Bibliotecă cu protecție |
|---|---|---|
| Type mismatch | DecodingError / excepție | kotlinx — coerceInputValues = true |
| Lipsă câmp | Cădere la accesare | Moshi — @Transient + default |
| Format de dată incorect | Eroare de decodare | JSONDecoder — dateDecodingStrategy |
| Câmpuri suplimentare | Ignorate sau cădere | kotlinx — ignoreUnknownKeys = true |
| Null în câmp non-null | Cădere în runtime | Moshi — lenient cu @Nullable |
Logarea erorilor de deserializare — practică obligatorie în producție. Înfășurați decode în do/catch, logați JSON-ul brut și tipul modelului așteptat în Crashlytics sau Sentry. Acest lucru va permite identificarea rapidă a câmpului, API-ului și versiunii aplicației care a cauzat problema. Fără logare, eroarea de deserializare apare ca o cădere misterioasă fără context.
Întrebări frecvente
Parsarea — analiza textului structurat în elemente componente fără a crea obligatoriu un model tipizat. Deserializarea este un caz particular al parsării, al cărui rezultat este un obiect complet al limbajului cu tipuri cunoscute de proprietăți. Parsarea poate fi flux, deserializarea creează întotdeauna un obiect complet.
Pentru un proiect în Kotlin pur, se recomandă kotlinx.serialization — este integrată în compilator, nu folosește reflection și suportă Kotlin Multiplatform. Pentru un proiect existent în Java — Moshi cu code generation. Gson este mai bine să fie păstrat pentru proiecte legacy, unde înlocuirea sa ar necesita eforturi semnificative.
Pe iOS, utilizați keyDecodingStrategy = .convertFromSnakeCase în JSONDecoder. Pe Android, în kotlinx.serialization, utilizați @SerialName pentru fiecare câmp. În Moshi, aplicați @Json(name=„field_name”) sau un JsonAdapter.Factory global. Un stil uniform la nivel de proiect este o best practice convenită în contractul API.
Cea mai frecventă cauză este null-ul neașteptat de la server pentru un câmp declarat obligatoriu. în dezvoltare, serverul returnează date complete, în producție — un răspuns scurtat. Soluția: marcați toate câmpurile potențial absente ca nullable (Kotlin) sau optional (Swift), utilizați ignoreUnknownKeys și valori implicite.
Code generation (Moshi codegen, kotlinx.serialization, Codable) funcționează de 2-4 ori mai rapid decât reflection în benchmark-urile Google. Pe lângă viteză, generarea de cod este mai sigură din punctul de vedere al tipurilor, nu necesită metadata claselor în runtime, iar erorile de tip sunt detectate în faza de compilare, nu în momentul deserializării.
Concluzii
Vom dezvolta o aplicație mobilă la cheie
IT Sectr creează aplicații iOS și Android pentru startup-uri și afaceri din 2017. Vă vom consilia și vă vom propune cea mai bună soluție.
Citiți și