JSONSerialization — una classe iOS integrata del framework Foundation progettata per convertire JSON in oggetti Foundation e viceversa. Questa API è il meccanismo di base per lavorare con JSON sulle piattaforme Apple senza librerie di terze parti, supportando l'analisi di dizionari, array e tipi primitivi. Secondo Apple Developer, 2024, JSONSerialization supporta il lavoro con Data, flussi e opzioni di lettura per l'elaborazione flessibile dei dati JSON.
Punti chiave
JSONSerialization è una classe del framework Foundation disponibile su iOS, macOS, tvOS e watchOS. Fornisce metodi per convertire Data JSON in oggetti Foundation (NSDictionary, NSArray, NSString, NSNumber) e viceversa. La classe è apparsa in iOS 5 e fino all'introduzione di Codable (Swift 4) è rimasta il modo principale per lavorare con JSON sulle piattaforme Apple. Nonostante l'età, JSONSerialization rimane rilevante nei progetti legacy in Objective-C e negli scenari che richiedono un'elaborazione JSON dinamica senza uno schema di modello fisso.
Nonostante l'avvento di Codable, JSONSerialization rimane rilevante in diversi scenari. Struttura JSON dinamica — quando il formato della risposta cambia o è sconosciuto in anticipo — richiede l'accesso ai dizionari tramite chiavi, cosa più facile da fare attraverso JSONSerialization. La classe viene utilizzata anche in progetti Objective-C dove Codable non è disponibile e quando si lavora con flussi per l'analisi incrementale di grandi file JSON. Nei test e nei mockup, isValidJSONObject e data(withJSONObject:options:) consentono di generare rapidamente fixture JSON senza librerie di terze parti, accelerando lo sviluppo e la prototipazione.
import Foundation
// Struttura di base dell'utilizzo di JSONSerialization
let jsonString = """
{
"id": 1,
"name": "John Doe",
"email": "john@example.com"
}
"""
guard let jsonData = jsonString.data(using: .utf8) else {
return
}
do {
let json = try JSONSerialization
.jsonObject(with: jsonData,
options: .mutableContainers)
print(json)
} catch {
print("Errore di analisi JSON: \(error)")
}
JSONSerialization fornisce quattro metodi principali per lavorare con JSON. Il metodo principale è jsonObject(with:options:), che converte Data in oggetti Foundation. Il metodo data(withJSONObject:options:) esegue la serializzazione inversa. isValidJSONObject(_:) verifica se un oggetto può essere serializzato. writeJSONObject(_:to:options:error:) scrive JSON direttamente in un flusso. Per leggere JSON da InputStream, esiste il metodo jsonObject(with:options:) che accetta un flusso invece di Data, comodo quando ci si integra con richieste di rete che restituiscono dati in streaming.
Il metodo jsonObject accetta Data e restituisce Any — generalmente NSDictionary o NSArray. Per un uso sicuro, il risultato viene convertito nel tipo previsto tramite casting condizionale. Il metodo data accetta un oggetto Foundation e restituisce Data con una rappresentazione JSON. L'opzione .prettyPrinted aggiunge la formattazione con indentazione per la leggibilità.
let jsonString = """
{
"products": [
{"id": 1, "name": "iPhone", "price": 999},
{"id": 2, "name": "iPad", "price": 799}
]
}
"""
let data = Data(jsonString.utf8)
if let json = try? JSONSerialization
.jsonObject(with: data) as? [String: Any],
let products = json["products"] as? [[String: Any]] {
for product in products {
if let name = product["name"] as? String {
print("Prodotto: \(name)")
}
}
}
// Serializzazione inversa: oggetto -> JSON
let outputDict: [String: Any] = ["status": "ok", "count": 42]
if let outputData = try? JSONSerialization
.data(withJSONObject: outputDict,
options: .prettyPrinted) {
String(data: outputData, encoding: .utf8)
}
Analisi di base di un dizionario con tipi primitivi è l'operazione più comune con JSONSerialization. Dopo aver ricevuto Data tramite URLSession, lo sviluppatore chiama jsonObject e converte il risultato nel tipo previsto. Per array di oggetti, si utilizza il casting a [[String: Any]], dopo di che ogni elemento viene elaborato in un ciclo. Questo approccio è flessibile ma richiede la gestione manuale dei tipi.
Le API reali restituiscono oggetti JSON annidati complessi con array, date e campi opzionali. JSONSerialization gestisce correttamente qualsiasi profondità di annidamento, ma lo sviluppatore deve convertire ogni livello al tipo richiesto in modo indipendente. Per semplificare questo compito, Apple raccomanda di utilizzare Codable per i dati tipizzati e JSONSerialization solo per le strutture dinamiche.
// Analisi della risposta API
func parseUserResponse(data: Data) {
do {
guard let json = try JSONSerialization
.jsonObject(with: data) as? [String: Any]
else { return }
guard let userId = json["id"] as? Int,
let name = json["name"] as? String
else {
throw ParsingError.missingField
}
print("Utente: \(name) (ID: \(userId))")
} catch let error as ParsingError {
print("Analisi fallita: \(error)")
} catch {
print("Errore imprevisto: \(error)")
}
}
enum ParsingError: Error {
case missingField
case invalidType
}
JSONSerialization genera errori in caso di JSON non valido, mancata corrispondenza dei tipi o superamento della profondità di annidamento. Gli errori appartengono al tipo CocoaError e contengono un codice che descrive il problema. Lo sviluppatore deve gestirli tramite una costruzione do-catch, altrimenti l'applicazione si bloccherà. Gli errori più comuni sono: NSPropertyListReadCorruptError (JSON non valido) e NSPropertyListReadUnknownError. Ogni tipo di errore richiede la propria strategia di gestione: per formato non valido, richiedere il reinvio dei dati e, per mancata corrispondenza della struttura, aggiornare il modello di analisi.
JSON non valido — la causa più comune di fallimenti: una virgola mancante, un carattere extra o una virgoletta non escapata rompe l'intera analisi. Il secondo tipo di errore è la mancata corrispondenza con la struttura prevista: ad esempio, il server ha restituito un array invece di un dizionario. JSONSerialization.fragmentsAllowed consente di leggere JSON la cui radice non è un dizionario o un array ma un valore primitivo. Lo sviluppatore può anche incontrare un errore di superamento della profondità di annidamento quando JSON contiene troppi livelli gerarchici.
JSONSerialization fornisce diverse opzioni per configurare l'analisi. .mutableContainers restituisce NSMutableDictionary e NSMutableArray invece delle versioni immutabili, utile quando si modificano i dati dopo l'analisi. .mutableLeaves rende mutabili i valori stringa. .fragmentsAllowed consente JSON la cui radice non è un oggetto o array ma una stringa o numero — comodo per risposte API semplici. Le opzioni .withoutEscapingSlashes e .sortedKeys sono disponibili per il metodo data(withJSONObject:options:), controllando la formattazione del JSON serializzato. Le opzioni vengono passate come maschera di bit, consentendo di combinare più valori tramite l'operatore | per una configurazione flessibile dell'analisi.
// Gestione di vari tipi di errori
func safeParse(jsonData: Data) {
do {
let object = try JSONSerialization
.jsonObject(with: jsonData,
options: .fragmentsAllowed)
if let dictionary = object as? [String: Any] {
print("Dizionario con \(dictionary.count) chiavi")
} else if let array = object as? [Any] {
print("Array con \(array.count) elementi")
}
} catch CocoaError.propertyListReadCorrupt {
print("Dati JSON corrotti")
} catch let error as CocoaError {
print("Errore Cocoa: \(error)")
} catch {
print("Errore sconosciuto: \(error)")
}
}
// Verifica della validità dell'oggetto prima della serializzazione
let testObject: [String: Any] = ["key": "value", "nested": ["a": 1]]
if JSONSerialization.isValidJSONObject(testObject) {
print("Oggetto JSON valido")
}
Le prestazioni di JSONSerialization dipendono dalla dimensione dei dati e dalla frequenza delle chiamate. Per una singola analisi di una piccola risposta del server, la differenza è trascurabile, ma quando si elaborano decine di megabyte di JSON o chiamate frequenti in cicli, l'overhead del casting dei tipi deve essere considerato. JSONSerialization funziona in modo sincrono nel thread corrente, quindi per documenti grandi si consiglia di spostare l'analisi in una coda in background tramite DispatchQueue.global(). In alternativa, è possibile utilizzare InputStream per l'elaborazione in streaming senza caricare l'intero file in memoria, il che è fondamentale per applicazioni con risorse limitate. Per scrivere JSON in un file o flusso di rete, il metodo writeJSONObject(_:to:options:error:) consente di inviare direttamente dati serializzati a OutputStream senza creare un oggetto Data intermedio, riducendo il consumo di memoria quando si lavora con documenti grandi.
Domande frequenti
JSONSerialization è una classe Foundation per convertire Data JSON in oggetti Foundation (NSDictionary, NSArray) e viceversa. Funziona su iOS, macOS, tvOS e watchOS senza librerie aggiuntive.
Codable è un protocollo Swift per la serializzazione tipizzata automatica che si compila in codice type-safe. JSONSerialization lavora con tipi dinamici Any e richiede casting manuale. Codable è preferibile per nuovi progetti, JSONSerialization per Objective-C e dati dinamici.
Utilizzare la costruzione do-catch quando si chiama jsonObject. Gli errori di JSONSerialization appartengono a CocoaError. Per il debug, controllare NSPropertyListReadCorruptError, che indica un formato dati JSON non valido.
Sì, JSONSerialization supporta qualsiasi profondità di annidamento di dizionari e array. Tutti gli oggetti annidati vengono convertiti nei corrispondenti tipi Foundation (NSDictionary, NSArray, NSString, NSNumber), preservando la struttura JSON originale.
JSONSerialization è appropriato per strutture JSON dinamiche, in progetti Objective-C, quando si lavora con flussi e per la validazione JSON tramite isValidJSONObject. Per strutture tipizzate con uno schema noto, Codable è preferibile.
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