Swift Language
Lettura e scrittura JSON
Ricerca…
Sintassi
- NSJSONSerialization.JSONObjectWithData (jsonData, options: NSJSONReadingOptions) // Restituisce un oggetto da jsonData. Questo metodo si basa sul fallimento.
- NSJSONSerialization.dataWithJSONObject (jsonObject, options: NSJSONWritingOptions) // Restituisce NSData da un oggetto JSON. Passa a NSJSONWritingOptions.PrettyStampato in opzioni per un output più leggibile.
Serializzazione, codifica e decodifica JSON con Apple Foundation e Swift Standard Library
La classe JSONSerialization è integrata nel framework Foundation di Apple.
Leggi JSON
La funzione JSONObjectWithData
prende NSData
e restituisce AnyObject
. Puoi usare as?
per convertire il risultato nel tipo atteso.
do {
guard let jsonData = "[\"Hello\", \"JSON\"]".dataUsingEncoding(NSUTF8StringEncoding) else {
fatalError("couldn't encode string as UTF-8")
}
// Convert JSON from NSData to AnyObject
let jsonObject = try NSJSONSerialization.JSONObjectWithData(jsonData, options: [])
// Try to convert AnyObject to array of strings
if let stringArray = jsonObject as? [String] {
print("Got array of strings: \(stringArray.joinWithSeparator(", "))")
}
} catch {
print("error reading JSON: \(error)")
}
Puoi passare le options: .AllowFragments
invece delle options: []
per consentire la lettura di JSON quando l'oggetto di livello superiore non è un array o un dizionario.
Scrivi JSON
Chiamando dataWithJSONObject
converte un oggetto compatibile con JSON (array nidificati o dizionari con stringhe, numeri e NSNull
) su NSData
raw codificato come UTF-8.
do {
// Convert object to JSON as NSData
let jsonData = try NSJSONSerialization.dataWithJSONObject(jsonObject, options: [])
print("JSON data: \(jsonData)")
// Convert NSData to String
let jsonString = String(data: jsonData, encoding: NSUTF8StringEncoding)!
print("JSON string: \(jsonString)")
} catch {
print("error writing JSON: \(error)")
}
È possibile passare options: .PrettyPrinted
anziché options: []
per la stampa carina.
Stesso comportamento in Swift 3 ma con una sintassi diversa.
do {
guard let jsonData = "[\"Hello\", \"JSON\"]".data(using: String.Encoding.utf8) else {
fatalError("couldn't encode string as UTF-8")
}
// Convert JSON from NSData to AnyObject
let jsonObject = try JSONSerialization.jsonObject(with: jsonData, options: [])
// Try to convert AnyObject to array of strings
if let stringArray = jsonObject as? [String] {
print("Got array of strings: \(stringArray.joined(separator: ", "))")
}
} catch {
print("error reading JSON: \(error)")
}
do {
// Convert object to JSON as NSData
let jsonData = try JSONSerialization.data(withJSONObject: jsonObject, options: [])
print("JSON data: \(jsonData)")
// Convert NSData to String
let jsonString = String(data: jsonData, encoding: .utf8)!
print("JSON string: \(jsonString)")
} catch {
print("error writing JSON: \(error)")
}
Nota: il seguente è attualmente disponibile solo in Swift 4.0 e versioni successive.
A partire da Swift 4.0, la libreria standard di Swift include i protocolli Encodable
e Decodable
per definire un approccio standardizzato alla codifica e alla decodifica dei dati. L'adozione di questi protocolli consentirà implementazioni dei protocolli Encoder
e Decoder
prendere i dati e codificarli o decodificarli da e verso una rappresentazione esterna come JSON. La conformità al protocollo Codable
combina entrambi i protocolli Encodable
e Decodable
. Questo è ora il mezzo raccomandato per gestire JSON nel tuo programma.
Codifica e decodifica automaticamente
Il modo più semplice per rendere un tipo codificabile è dichiarare le sue proprietà come tipi già Codable
. Questi tipi includono tipi di libreria standard come String
, Int
e Double
; e tipi di base come Date
, Data
e URL
. Se le proprietà di un tipo sono codificabili, il tipo stesso si conformerà automaticamente a Codable
semplicemente dichiarando la conformità.
Si consideri il seguente esempio, in cui la struttura del Book
è conforme al Codable
.
struct Book: Codable {
let title: String
let authors: [String]
let publicationDate: Date
}
Nota che le raccolte standard come
Array
eDictionary
conformi aCodable
se contengono tipi codificabili.
Adottando Codable
, la struttura del Book
può ora essere codificata e decodificata da JSON utilizzando le classi di Apple Foundation JSONEncoder
e JSONDecoder
, anche se Book
stesso non contiene codice per gestire specificamente JSON. Anche i codificatori e decodificatori personalizzati possono essere scritti, rispettando i protocolli Encoder
e Decoder
, rispettivamente.
Codifica dati JSON
// Create an instance of Book called book
let encoder = JSONEncoder()
let data = try! encoder.encode(book) // Do not use try! in production code
print(data)
Imposta
encoder.outputFormatting = .prettyPrinted
per facilitare la lettura. ## Decodifica dai dati JSON
Decodifica dai dati JSON
// Retrieve JSON string from some source
let jsonData = jsonString.data(encoding: .utf8)!
let decoder = JSONDecoder()
let book = try! decoder.decode(Book.self, for: jsonData) // Do not use try! in production code
print(book)
Nell'esempio sopra,
Book.self
informa il decodificatore del tipo a cui il JSON deve essere decodificato.
Codifica o decodifica in esclusiva
A volte potrebbe non essere necessario che i dati siano entrambi codificabili e decodificabili, ad esempio quando è necessario leggere solo i dati JSON da un'API o se il programma invia solo dati JSON a un'API.
Se si intende solo scrivere dati JSON, conformare il proprio tipo a Encodable
.
struct Book: Encodable {
let title: String
let authors: [String]
let publicationDate: Date
}
Se si intende solo leggere i dati JSON, conformare il proprio tipo a Decodable
.
struct Book: Decodable {
let title: String
let authors: [String]
let publicationDate: Date
}
Utilizzo dei nomi chiave personalizzati
Le API utilizzano spesso convenzioni di denominazione diverse dal caso cammello standard Swift, ad esempio il caso serpente. Questo può diventare un problema quando si tratta di decodificare JSON, poiché per impostazione predefinita le chiavi JSON devono essere allineate esattamente con i nomi delle proprietà del tipo. Per gestire questi scenari è possibile creare chiavi personalizzate per il proprio tipo utilizzando il protocollo CodingKey
.
struct Book: Codable {
// ...
enum CodingKeys: String, CodingKey {
case title
case authors
case publicationDate = "publication_date"
}
}
CodingKeys
vengono generati automaticamente per i tipi che adottano il protocollo Codable
, ma creando la nostra implementazione nell'esempio precedente, permettiamo al nostro decodificatore di abbinare la publicationDate
caso cammello publicationDate
con il caso snake publication_date
come viene fornito dall'API.
SwiftyJSON
SwiftyJSON è un framework Swift creato per rimuovere la necessità di concatenamento opzionale nella normale serializzazione JSON.
Puoi scaricarlo qui: https://github.com/SwiftyJSON/SwiftyJSON
Senza SwiftyJSON, il tuo codice sarebbe simile a questo per trovare il nome del primo libro in un oggetto JSON:
if let jsonObject = try NSJSONSerialization.JSONObjectWithData(data, options: .AllowFragments) as? [[String: AnyObject]],
let bookName = (jsonObject[0]["book"] as? [String: AnyObject])?["name"] as? String {
//We can now use the book name
}
In SwiftyJSON, questo è estremamente semplificato:
let json = JSON(data: data)
if let bookName = json[0]["book"]["name"].string {
//We can now use the book name
}
Rimuove la necessità di controllare ogni campo, in quanto restituirà nulla se nessuno di essi è valido.
Per usare SwiftyJSON, scarica la versione corretta dal repository Git - c'è un ramo per Swift 3. Basta trascinare il "SwiftyJSON.swift" nel tuo progetto e importarlo nella tua classe:
import SwiftyJSON
Puoi creare il tuo oggetto JSON usando i seguenti due inizializzatori:
let jsonObject = JSON(data: dataObject)
o
let jsonObject = JSON(jsonObject) //This could be a string in a JSON format for example
Per accedere ai tuoi dati, usa gli indici:
let firstObjectInAnArray = jsonObject[0]
let nameOfFirstObject = jsonObject[0]["name"]
È quindi possibile analizzare il valore su un determinato tipo di dati, che restituirà un valore facoltativo:
let nameOfFirstObject = jsonObject[0]["name"].string //This will return the name as a string
let nameOfFirstObject = jsonObject[0]["name"].double //This will return null
Puoi anche compilare i tuoi percorsi in una matrice rapida:
let convolutedPath = jsonObject[0]["name"][2]["lastName"]["firstLetter"].string
Equivale a:
let convolutedPath = jsonObject[0, "name", 2, "lastName", "firstLetter"].string
SwiftyJSON ha anche funzionalità per stampare i propri errori:
if let name = json[1337].string {
//You can use the value - it is valid
} else {
print(json[1337].error) // "Array[1337] is out of bounds" - You cant use the value
}
Se hai bisogno di scrivere sul tuo oggetto JSON, puoi usare di nuovo gli abbonati:
var originalJSON:JSON = ["name": "Jack", "age": 18]
originalJSON["age"] = 25 //This changes the age to 25
originalJSON["surname"] = "Smith" //This creates a new field called "surname" and adds the value to it
Se hai bisogno della stringa originale per il JSON, ad esempio se devi scriverlo in un file, puoi ottenere il valore grezzo:
if let string = json.rawString() { //This is a String object
//Write the string to a file if you like
}
if let data = json.rawData() { //This is an NSData object
//Send the data to your server if you like
}
Freddy
Freddy è una libreria di analisi JSON gestita da Big Nerd Ranch . Ha tre principali vantaggi:
Sicurezza del tipo: ti aiuta a lavorare con l'invio e la ricezione di JSON in modo da prevenire arresti anomali del runtime.
Idiomatico: sfrutta i generici, le enumerazioni e le funzionalità funzionali di Swift, senza complicata documentazione o magici operatori personalizzati.
Gestione degli errori: fornisce informazioni di errore informative per gli errori JSON comunemente presenti.
Esempio di dati JSON
Definiamo alcuni dati JSON di esempio da utilizzare con questi esempi.
{ "success": true, "people": [ { "name": "Matt Mathias", "age": 32, "spouse": true }, { "name": "Sergeant Pepper", "age": 25, "spouse": false } ], "jobs": [ "teacher", "judge" ], "states": { "Georgia": [ 30301, 30302, 30303 ], "Wisconsin": [ 53000, 53001 ] } }
let jsonString = "{\"success\": true, \"people\": [{\"name\": \"Matt Mathias\",\"age\": 32,\"spouse\": true},{\"name\": \"Sergeant Pepper\",\"age\": 25,\"spouse\": false}],\"jobs\": [\"teacher\",\"judge\"],\"states\": {\"Georgia\": [30301,30302,30303],\"Wisconsin\": [53000,53001]}}" let jsonData = jsonString.dataUsingEncoding(NSUTF8StringEncoding)!
Deserializzazione dei dati grezzi
Per deserializzare i dati, inizializziamo un oggetto JSON
quindi accediamo a una particolare chiave.
do { let json = try JSON(data: jsonData) let success = try json.bool("success") } catch { // do something with the error }
try
qui perché l'accesso a json
per la chiave "success"
potrebbe fallire - potrebbe non esistere, o il valore potrebbe non essere un booleano.
Possiamo anche specificare un percorso per accedere agli elementi nidificati nella struttura JSON. Il percorso è un elenco separato da virgole di chiavi e indici che descrive il percorso verso un valore di interesse.
do { let json = try JSON(data: jsonData) let georgiaZipCodes = try json.array("states", "Georgia") let firstPersonName = try json.string("people", 0, "name") } catch { // do something with the error }
Deserializzare i modelli direttamente
JSON può essere analizzato direttamente in una classe del modello che implementa il protocollo JSONDecodable
.
public struct Person { public let name: String public let age: Int public let spouse: Bool } extension Person: JSONDecodable { public init(json: JSON) throws { name = try json.string("name") age = try json.int("age") spouse = try json.bool("spouse") } } do { let json = try JSON(data: jsonData) let people = try json.arrayOf("people", type: Person.self) } catch { // do something with the error }
Serializzazione di dati grezzi
Qualsiasi valore JSON
può essere serializzato direttamente su NSData
.
let success = JSON.Bool(false) let data: NSData = try success.serialize()
Serializzare i modelli direttamente
Qualsiasi classe di modello che implementa il protocollo JSONEncodable
può essere serializzata direttamente su NSData
.
extension Person: JSONEncodable { public func toJSON() -> JSON { return .Dictionary([ "name": .String(name), "age": .Int(age), "spouse": .Bool(spouse) ]) } } let newPerson = Person(name: "Glenn", age: 23, spouse: true) let data: NSData = try newPerson.toJSON().serialize()
Freccia
Arrow è un'elegante libreria di analisi JSON in Swift.
Permette di analizzare JSON e associarlo a classi di modelli personalizzati con l'aiuto di un operatore <--
:
identifier <-- json["id"]
name <-- json["name"]
stats <-- json["stats"]
Esempio:
Modello Swift
struct Profile {
var identifier = 0
var name = ""
var link: NSURL?
var weekday: WeekDay = .Monday
var stats = Stats()
var phoneNumbers = [PhoneNumber]()
}
File JSON
{
"id": 15678,
"name": "John Doe",
"link": "https://apple.com/steve",
"weekdayInt" : 3,
"stats": {
"numberOfFriends": 163,
"numberOfFans": 10987
},
"phoneNumbers": [{
"label": "house",
"number": "9809876545"
}, {
"label": "cell",
"number": "0908070656"
}, {
"label": "work",
"number": "0916570656"
}]
}
Mappatura
extension Profile: ArrowParsable {
mutating func deserialize(json: JSON) {
identifier <-- json["id"]
link <-- json["link"]
name <-- json["name"]
weekday <-- json["weekdayInt"]
stats <- json["stats"]
phoneNumbers <-- json["phoneNumbers"]
}
}
uso
let profile = Profile()
profile.deserialize(json)
Installazione:
Carthage
github "s4cha/Arrow"
CocoaPods
pod 'Arrow'
use_frameworks!
manualmente
Basta copiare e incollare Arrow.swift nel tuo progetto Xcode
https://github.com/s4cha/Arrow
Come un quadro
Scarica Arrow dal repository GitHub e crea l'obiettivo Framework nel progetto di esempio. Quindi Link contro questo framework.
Semplice JSON che analizza gli oggetti personalizzati
Anche se le librerie di terze parti sono buone, un semplice modo per analizzare il JSON è fornito da protocolli. Puoi immaginare di avere un oggetto Todo
come
struct Todo {
let comment: String
}
Ogni volta che si riceve il JSON, è possibile gestire il normale NSData
come mostrato nell'altro esempio usando NSJSONSerialization
oggetto NSJSONSerialization
.
Successivamente, utilizzando un semplice protocollo JSONDecodable
typealias JSONDictionary = [String:AnyObject]
protocol JSONDecodable {
associatedtype Element
static func from(json json: JSONDictionary) -> Element?
}
E il trucco è rendere la struttura di Todo
conforme a JSONDecodable
extension Todo: JSONDecodable {
static func from(json json: JSONDictionary) -> Todo? {
guard let comment = json["comment"] as? String else { return nil }
return Todo(comment: comment)
}
}
Puoi provarlo con questo codice JSON:
{
"todos": [
{
"comment" : "The todo comment"
}
]
}
Quando l'hai ottenuto dall'API, puoi serializzarlo come gli esempi precedenti mostrati in un'istanza AnyObject
. Successivamente, è possibile verificare se l'istanza è un'istanza di JSONDictionary
guard let jsonDictionary = dictionary as? JSONDictionary else { return }
L'altra cosa da controllare, specifica per questo caso perché hai un array di Todo
nel JSON, è il dizionario di todos
i todos
guard let todosDictionary = jsonDictionary["todos"] as? [JSONDictionary] else { return }
Ora che hai la gamma di dizionari, puoi convertirli in un oggetto Todo
usando flatMap
(cancellerà automaticamente i valori nil
dall'array)
let todos: [Todo] = todosDictionary.flatMap { Todo.from(json: $0) }
JSON Parsing Swift 3
Ecco il file JSON che useremo chiamato animals.json
{
"Sea Animals": [
{
"name": "Fish",
"question": "How many species of fish are there?" },
{
"name": "Sharks",
"question": "How long do sharks live?"
},
{
"name": "Squid",
"question": "Do squids have brains?"
},
{
"name": "Octopus",
"question": "How big do octopus get?"
},
{
"name": "Star Fish",
"question": "How long do star fish live?"
}
],
"mammals": [
{
"name": "Dog",
"question": "How long do dogs live?"
},
{
"name": "Elephant",
"question": "How much do baby elephants weigh?"
},
{
"name": "Cats",
"question": "Do cats really have 9 lives?"
},
{
"name": "Tigers",
"question": "Where do tigers live?"
},
{
"name": "Pandas",
"question": "WHat do pandas eat?"
} ] }
Importa il tuo file JSON nel tuo progetto
È possibile eseguire questa semplice funzione per stampare il file JSON
func jsonParsingMethod() {
//get the file
let filePath = Bundle.main.path(forResource: "animals", ofType: "json")
let content = try! String(contentsOfFile: filePath!)
let data: Data = content.data(using: String.Encoding.utf8)!
let json: NSDictionary = try! JSONSerialization.jsonObject(with: data as Data, options:.mutableContainers) as! NSDictionary
//Call which part of the file you'd like to pare
if let results = json["mammals"] as? [[String: AnyObject]] {
for res in results {
//this will print out the names of the mammals from out file.
if let rates = res["name"] as? String {
print(rates)
}
}
}
}
Se vuoi metterlo in una vista tabella, vorrei prima creare un dizionario con un NSObject.
Crea un nuovo file rapido chiamato ParsingObject
e crea le tue variabili stringa.
Assicurarsi che il nome della variabile sia lo stesso del file JSON
. Ad esempio, nel nostro progetto abbiamo name
e question
così nel nostro nuovo file swift, che useremo
var name: String?
var question: String?
Inizializza il NSObject che abbiamo creato nel nostro ViewController.swift var array = ParsingObject Quindi eseguiremo lo stesso metodo che avevamo prima con una piccola modifica.
func jsonParsingMethod() {
//get the file
let filePath = Bundle.main.path(forResource: "animals", ofType: "json")
let content = try! String(contentsOfFile: filePath!)
let data: Data = content.data(using: String.Encoding.utf8)!
let json: NSDictionary = try! JSONSerialization.jsonObject(with: data as Data, options:.mutableContainers) as! NSDictionary
//This time let's get Sea Animals
let results = json["Sea Animals"] as? [[String: AnyObject]]
//Get all the stuff using a for-loop
for i in 0 ..< results!.count {
//get the value
let dict = results?[i]
let resultsArray = ParsingObject()
//append the value to our NSObject file
resultsArray.setValuesForKeys(dict!)
array.append(resultsArray)
}
}
Quindi lo mostriamo nella nostra tableview facendo questo,
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return array.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)
//This is where our values are stored
let object = array[indexPath.row]
cell.textLabel?.text = object.name
cell.detailTextLabel?.text = object.question
return cell
}