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.

2.2

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.

3.0

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 e Dictionary conformi a Codable 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:

  1. Sicurezza del tipo: ti aiuta a lavorare con l'invio e la ricezione di JSON in modo da prevenire arresti anomali del runtime.

  2. Idiomatico: sfrutta i generici, le enumerazioni e le funzionalità funzionali di Swift, senza complicata documentazione o magici operatori personalizzati.

  3. 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
        }


Modified text is an extract of the original Stack Overflow Documentation
Autorizzato sotto CC BY-SA 3.0
Non affiliato con Stack Overflow