La serializzazione e la deserializzazione sono processi fondamentali di conversione degli oggetti in un formato per la trasmissione o l'archiviazione. Nello sviluppo mobile, questi meccanismi vengono utilizzati in ogni richiesta di rete, salvataggio dello stato e comunicazione tra processi. Secondo MDN Web Docs, 2024, JSON rimane il formato più popolare di serializzazione nel web e nelle applicazioni mobili, superando XML e Protocol Buffers.
Punti chiave
La serializzazione è il processo di conversione di un oggetto applicativo in un formato adatto alla trasmissione in rete o all'archiviazione su disco. La deserializzazione esegue la conversione inversa, ripristinando l'oggetto dai dati ricevuti. Nello sviluppo mobile, la serializzazione viene utilizzata nelle richieste API, nel salvataggio dello stato dello schermo, nella memorizzazione nella cache e nel trasferimento dei dati tramite Intent o Bundle.
Qualsiasi interazione tra componenti dell'applicazione o tra l'applicazione e il server richiede la serializzazione. Le API REST trasmettono dati in JSON o XML, gRPC utilizza Protocol Buffers e la comunicazione intraprocesso su Android utilizza Parcelable. Senza serializzazione, è impossibile passare un oggetto complesso attraverso il confine di un processo o salvarlo in un database.
// Principio generale di serializzazione
data class User(
val id: Int,
val name: String,
val email: String
)
// Serializzazione: oggetto -> JSON
fun serializeUser(user: User): String {
return """{"id":${user.id},"name":"${user.name}","email":"${user.email}"}"""
}
// Deserializzazione: JSON -> oggetto
fun deserializeUser(json: String): User? {
// analisi di JSON in oggetto
return Gson().fromJson(json, User::class.java)
}
| Formato | Dimensione | Velocità | Leggibilità | Tipizzazione |
|---|---|---|---|---|
| JSON | media | alta | alta | dinamica |
| XML | grande | media | alta | schema XSD |
| Protocol Buffers | piccola | molto alta | bassa | stretto .proto |
| FlatBuffers | piccola | massima | bassa | stretto .fbs |
JSON (JavaScript Object Notation) è un formato di testo leggero basato sulla sintassi degli oggetti JavaScript. JSON supporta stringhe, numeri, valori booleani, array e oggetti annidati, coprendo la maggior parte degli scenari di trasferimento dati nelle applicazioni mobili. Il formato è indipendente dalla piattaforma: ogni ecosistema fornisce strumenti integrati per la sua analisi.
JSON vince per la sua semplicità e versatilità. Lo sviluppatore non ha bisogno di uno schema per l'uso di base — la struttura viene determinata dinamicamente durante l'analisi. La leggibilità del formato semplifica il debug e i test: la risposta del server può essere visualizzata in qualsiasi strumento di sviluppo. Su entrambe le piattaforme mobili, JSON viene elaborato nativamente senza la necessità di librerie di terze parti.
// Codable — serializzazione JSON nativa in 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) è un formato di serializzazione binario sviluppato da Google per sistemi ad alte prestazioni. A differenza di JSON, protobuf richiede uno schema predefinito in un file .proto, ma fornisce una dimensione dei dati significativamente inferiore e una maggiore velocità di elaborazione. Il formato è utilizzato in gRPC, Firebase Firestore e nei servizi interni di Google.
Uno schema protobuf descrive messaggi con campi tipizzati, ciascuno con un numero univoco. Il compilatore protoc genera classi nel linguaggio di destinazione che eseguono automaticamente la serializzazione e la deserializzazione. Protobuf supporta l'evoluzione dello schema attraverso regole per aggiungere e rimuovere campi senza perdere la compatibilità all'indietro.
// Definizione dello schema in user.proto
syntax = "proto3";
message User {
int32 id = 1;
string name = 2;
string email = 3;
repeated string roles = 4;
}
// Codice generato in Kotlin
val user = UserProto.User.newBuilder()
.setId(1)
.setName("John")
.setEmail("john@test.com")
.build()
val bytes: ByteArray = user.toByteArray()
XML (eXtensible Markup Language) è un formato con struttura rigida, supporto per namespace e validazione tramite schemi XSD. XML perde rispetto a JSON in compattezza e velocità di analisi, ma rimane richiesto nello sviluppo Android per file di layout, AndroidManifest, risorse e configurazioni Gradle. Su iOS, XML viene utilizzato nei file plist e in alcuni servizi legacy.
Android offre tre approcci per l'analisi XML: DOM (caricamento dell'intero documento in memoria), SAX (analisi di flusso basata su eventi) e XmlPullParser (un approccio ibrido). XmlPullParser è l'opzione consigliata per i dispositivi mobili, poiché funziona in modo efficiente con memoria limitata e consente di elaborare il documento durante la lettura.
// XmlPullParser su Android
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!!)
}
Ogni piattaforma offre i propri strumenti di serializzazione. Su iOS, il meccanismo principale è il protocollo Codable con JSONEncoder e JSONDecoder, mentre Objective-C utilizza NSJSONSerialization. Android utilizza le librerie Gson, Moshi e kotlinx.serialization. Per il trasferimento di dati tra processi, Android utilizza Parcelable, mentre iOS utilizza NSKeyedArchiver. I progetti Kotlin scelgono sempre più spesso kotlinx.serialization — una soluzione di JetBrains che supporta la multipiattaforma e non dipende dalla riflessione, generando serializzatori in fase di compilazione tramite il plugin del compilatore Kotlin.
Android supporta due meccanismi di serializzazione per Intent e Bundle. Serializable è un meccanismo Java standard che utilizza la riflessione, portando a prestazioni inferiori. Parcelable è un protocollo specifico di Android che richiede l'implementazione manuale dei metodi writeToParcel e createFromParcel, ma funziona decine di volte più velocemente grazie alla manipolazione diretta dei byte.
// Parcelable su Android
@Parcelize
data class UserParcel(
val id: Int,
val name: String,
val email: String
) : Parcelable
// Trasferimento tramite Intent
val intent = Intent(this, DetailActivity::class.java)
intent.putExtra("user", userParcel)
startActivity(intent)
Su iOS, Codable con serializzazione preliminare in Data viene utilizzato per il trasferimento di dati tra controller, mentre NSKeyedArchiver viene utilizzato per la comunicazione tra processi, convertendo gli oggetti nel formato binario Property List. I progetti SwiftUI moderni preferiscono codificare i dati tramite JSONEncoder per il trasferimento tra moduli dell'applicazione.
La deserializzazione errata di dati non attendibili può portare a vulnerabilità. Gli attacchi tramite serializzazione sfruttano i metodi readObject sovrascritti in Java o deserializzatori non sicuri di librerie di terze parti. Su Android, è necessario evitare Serializable per dati provenienti da fonti non attendibili, utilizzando invece Parcelable o la validazione manuale di tutti i campi dopo la deserializzazione. Su iOS, JSONDecoder è rigoroso per impostazione predefinita con i tipi, ma quando si lavora con JSONSerialization, gli sviluppatori devono verificare i tipi tramite casting condizionale.
Quando si sceglie un formato di serializzazione, vengono considerati i requisiti di prestazioni, la dimensione dei dati e la compatibilità. Per le API REST e l'architettura a microservizi, JSON rimane la scelta ottimale — è supportato da tutte le piattaforme e linguaggi. Per i sistemi ad alto carico e le applicazioni mobili con larghezza di banda limitata, sono preferibili Protocol Buffers, che offrono dimensioni inferiori e deserializzazione più rapida. XML è giustificato solo in scenari di configurazione e nell'integrazione con sistemi legacy. I progetti moderni stanno anche assistendo all'ascesa di FlatBuffers — un formato binario senza fase di deserializzazione, utilizzato nei motori di gioco e nelle applicazioni con latenza minima. Ogni formato ha la sua nicchia e la scelta corretta influisce direttamente sulla velocità dell'applicazione e sul volume di traffico consumato.
Domande frequenti
La serializzazione è l'impacchettamento di un oggetto applicativo in un formato che può essere inviato su una rete o salvato in un file. Immagina di scattare una foto di un oggetto — la foto è la serializzazione e ripristinare l'oggetto dalla foto è la deserializzazione.
JSON è un formato di testo leggibile dall'uomo che non richiede uno schema. Protocol Buffers è un formato binario con uno schema obbligatorio (.proto), dimensioni significativamente inferiori e velocità maggiore. Protobuf viene scelto per sistemi ad alto carico, mentre JSON viene scelto per la compatibilità universale.
XML rimane lo standard per i file di configurazione (AndroidManifest, risorse di layout), documenti con annidamento complesso e sistemi con validazione rigorosa tramite XSD. XML viene utilizzato anche nei protocolli SOAP e nei sistemi legacy che richiedono il supporto dei namespace.
Codable è un protocollo Swift che combina Encodable e Decodable per la serializzazione e deserializzazione automatiche. Il compilatore genera l'implementazione dei metodi encode(to:) e init(from:) per tutte le proprietà della struttura o della classe.
Parcelable funziona significativamente più velocemente di Serializable perché non utilizza la riflessione e scrive i campi direttamente in un flusso di byte. Sui dispositivi moderni, la differenza può raggiungere un'accelerazione di 10 volte a favore di Parcelable.
Riepilogo
Svilupperemo un'applicazione mobile chiavi in mano
IT Sectr crea applicazioni iOS e Android per startup e aziende dal 2017. Ti consulteremo e ti proporremo la soluzione migliore.
Leggi anche