Swift Language
Lecture et écriture JSON
Recherche…
Syntaxe
- NSJSONSerialization.JSONObjectWithData (jsonData, options: NSJSONReadingOptions) // Renvoie un objet à partir de jsonData. Cette méthode échoue.
- NSJSONSerialization.dataWithJSONObject (jsonObject, options: NSJSONWritingOptions) // Renvoie NSData à partir d'un objet JSON. Transmettez NSJSONWritingOptions.PrettyPrinted dans les options pour une sortie plus lisible.
Sérialisation JSON, encodage et décodage avec Apple Foundation et la bibliothèque Swift Standard
La classe JSONSerialization est intégrée au framework Foundation d'Apple.
Lire JSON
La fonction JSONObjectWithData
prend NSData
et renvoie AnyObject
. Vous pouvez utiliser as?
pour convertir le résultat à votre type attendu.
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)")
}
Vous pouvez passer des options: .AllowFragments
au lieu des options: []
pour autoriser la lecture de JSON lorsque l'objet de niveau supérieur n'est pas un tableau ou un dictionnaire.
Ecrire JSON
L'appel de dataWithJSONObject
convertit un objet compatible JSON (tableaux imbriqués ou dictionnaires avec des chaînes, des nombres et NSNull
) en NSData
brut codé en 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)")
}
Vous pouvez passer des options: .PrettyPrinted
au lieu des options: []
pour une jolie impression.
Même comportement dans Swift 3 mais avec une syntaxe différente.
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)")
}
Remarque: La section suivante est actuellement disponible uniquement dans Swift 4.0 et versions ultérieures.
À partir de Swift 4.0, la bibliothèque standard Swift inclut les protocoles Encodable
et Decodable
pour définir une approche normalisée du codage et du décodage des données. L'adoption de ces protocoles permettra aux implémentations des protocoles Encoder
et Decoder
prendre vos données et de les encoder ou les décoder depuis et vers une représentation externe telle que JSON. La conformité au protocole Codable
combine les protocoles Encodable
et Decodable
. C'est maintenant le moyen recommandé de gérer JSON dans votre programme.
Encoder et décoder automatiquement
La méthode la plus simple pour rendre un type codable consiste à déclarer ses propriétés comme des types déjà Codable
. Ces types incluent les types de bibliothèque standard tels que String
, Int
et Double
; et les types de Fondation tels que Date
, Data
et URL
. Si les propriétés d'un type sont codables, le type lui-même se conformera automatiquement à Codable
en déclarant simplement la conformité.
Prenons l'exemple suivant, dans lequel la structure du Book
est conforme à Codable
.
struct Book: Codable {
let title: String
let authors: [String]
let publicationDate: Date
}
Notez que les collections standard telles que
Array
etDictionary
sont conformes àCodable
si elles contiennent des types codables.
En adoptant Codable
, la structure Book
peut maintenant être codée et décodée à partir de JSON en utilisant les classes Apple Foundation JSONEncoder
et JSONDecoder
, même si Book
lui-même ne contient aucun code pour gérer spécifiquement JSON. Les codeurs et décodeurs personnalisés peuvent également être écrits en respectant respectivement les protocoles de Encoder
et de Decoder
.
Encoder les données 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)
Définissez
encoder.outputFormatting = .prettyPrinted
pour faciliter la lecture. ## Décoder à partir de données JSON
Décoder à partir de données 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)
Dans l'exemple ci-dessus,
Book.self
informe le décodeur du type auquel le JSON doit être décodé.
Encodage ou décodage exclusivement
Parfois, vous n'avez peut-être pas besoin de données codables et décodables, par exemple lorsque vous avez uniquement besoin de lire des données JSON à partir d'une API ou que votre programme ne soumet que des données JSON à une API.
Si vous avez l'intention d'écrire uniquement des données JSON, conformez-vous à Encodable
.
struct Book: Encodable {
let title: String
let authors: [String]
let publicationDate: Date
}
Si vous souhaitez uniquement lire les données JSON, conformez-vous à Decodable
.
struct Book: Decodable {
let title: String
let authors: [String]
let publicationDate: Date
}
Utilisation de noms de clé personnalisés
Les API utilisent fréquemment des conventions de dénomination autres que le cas camel standard Swift, tel que le cas du serpent. Cela peut devenir un problème quand il s'agit de décoder JSON, car par défaut les clés JSON doivent s'aligner exactement sur les noms de propriété de votre type. Pour gérer ces scénarios, vous pouvez créer des clés personnalisées pour votre type à l'aide du protocole CodingKey
.
struct Book: Codable {
// ...
enum CodingKeys: String, CodingKey {
case title
case authors
case publicationDate = "publication_date"
}
}
CodingKeys
sont générés automatiquement pour les types qui adoptent le protocole Codable
, mais en créant notre propre implémentation dans l'exemple ci-dessus, nous Codable
notre décodeur à correspondre à la publicationDate
cas camel avec le cas de publication_date
tel qu'il est fourni par l'API.
SwiftyJSON
SwiftyJSON est un framework Swift conçu pour supprimer le chaînage facultatif dans la sérialisation JSON normale.
Vous pouvez le télécharger ici: https://github.com/SwiftyJSON/SwiftyJSON
Sans SwiftyJSON, votre code ressemblerait à ceci pour trouver le nom du premier livre dans un objet 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
}
Dans SwiftyJSON, ceci est extrêmement simplifié:
let json = JSON(data: data)
if let bookName = json[0]["book"]["name"].string {
//We can now use the book name
}
Cela supprime la nécessité de vérifier chaque champ, car il renverra zéro si l'un d'entre eux est invalide.
Pour utiliser SwiftyJSON, téléchargez la version correcte depuis le dépôt Git - il existe une branche pour Swift 3. Faites simplement glisser le "SwiftyJSON.swift" dans votre projet et importez-le dans votre classe:
import SwiftyJSON
Vous pouvez créer votre objet JSON en utilisant les deux initialiseurs suivants:
let jsonObject = JSON(data: dataObject)
ou
let jsonObject = JSON(jsonObject) //This could be a string in a JSON format for example
Pour accéder à vos données, utilisez les indices:
let firstObjectInAnArray = jsonObject[0]
let nameOfFirstObject = jsonObject[0]["name"]
Vous pouvez ensuite analyser votre valeur à un certain type de données, qui renverra une valeur facultative:
let nameOfFirstObject = jsonObject[0]["name"].string //This will return the name as a string
let nameOfFirstObject = jsonObject[0]["name"].double //This will return null
Vous pouvez également compiler vos chemins dans un tableau rapide:
let convolutedPath = jsonObject[0]["name"][2]["lastName"]["firstLetter"].string
Est le même que:
let convolutedPath = jsonObject[0, "name", 2, "lastName", "firstLetter"].string
SwiftyJSON a également des fonctionnalités pour imprimer ses propres erreurs:
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
}
Si vous devez écrire sur votre objet JSON, vous pouvez utiliser à nouveau les indices:
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
Si vous avez besoin de la chaîne d'origine pour JSON, par exemple si vous devez l'écrire dans un fichier, vous pouvez obtenir la valeur brute:
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 est une bibliothèque d'analyse JSON gérée par Big Nerd Ranch . Il présente trois avantages principaux:
Type Safety: vous aide à travailler avec l'envoi et la réception de JSON de manière à éviter les pannes à l'exécution.
Idiomatic: profite des génériques, des énumérations et des fonctionnalités de Swift, sans documentation compliquée ni opérateurs personnalisés magiques.
Gestion des erreurs: Fournit des informations d'erreur informatives pour les erreurs JSON courantes.
Exemple de données JSON
Définissons quelques exemples de données JSON à utiliser avec ces exemples.
{ "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)!
Désérialisation des données brutes
Pour désérialiser les données, nous initialisons un objet JSON
puis accédons à une clé particulière.
do { let json = try JSON(data: jsonData) let success = try json.bool("success") } catch { // do something with the error }
Nous try
ici parce que l'accès à json
pour la clé "success"
pourrait échouer - il pourrait ne pas exister, ou la valeur pourrait ne pas être un booléen.
Nous pouvons également spécifier un chemin d'accès aux éléments imbriqués dans la structure JSON. Le chemin est une liste de clés et d'indices séparés par des virgules qui décrivent le chemin d'accès à une valeur d'intérêt.
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 }
Désérialisation des modèles directement
JSON peut être directement analysé dans une classe de modèle qui implémente le protocole 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 }
Sérialisation des données brutes
Toute valeur JSON
peut être sérialisée directement à NSData
.
let success = JSON.Bool(false) let data: NSData = try success.serialize()
Sérialisation de modèles directement
Toute classe de modèle qui implémente le protocole JSONEncodable
peut être sérialisée directement dans 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()
Flèche
Arrow est une bibliothèque d'analyse JSON élégante dans Swift.
Il permet d’analyser JSON et de le mapper avec des classes de modèles personnalisées à l’aide d’un opérateur <--
:
identifier <-- json["id"]
name <-- json["name"]
stats <-- json["stats"]
Exemple:
Modèle rapide
struct Profile {
var identifier = 0
var name = ""
var link: NSURL?
var weekday: WeekDay = .Monday
var stats = Stats()
var phoneNumbers = [PhoneNumber]()
}
Fichier 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"
}]
}
Cartographie
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"]
}
}
Usage
let profile = Profile()
profile.deserialize(json)
Installation:
Carthage
github "s4cha/Arrow"
CacaoPods
pod 'Arrow'
use_frameworks!
Manuellement
Copiez et collez simplement Arrow.swift dans votre projet Xcode
https://github.com/s4cha/Arrow
Comme cadre
Téléchargez Arrow depuis le référentiel GitHub et créez la cible Framework sur le projet exemple. Puis lien contre ce cadre.
Analyse JSON simple dans des objets personnalisés
Même si les bibliothèques tierces sont bonnes, un moyen simple d’analyser le JSON est fourni par les protocoles. Vous pouvez imaginer que vous avez un objet Todo
as
struct Todo {
let comment: String
}
Chaque fois que vous recevez le JSON, vous pouvez gérer les NSData
comme indiqué dans l'autre exemple utilisant l'objet NSJSONSerialization
.
Après cela, en utilisant un protocole simple JSONDecodable
typealias JSONDictionary = [String:AnyObject]
protocol JSONDecodable {
associatedtype Element
static func from(json json: JSONDictionary) -> Element?
}
Et rendre votre structure Todo
conforme à JSONDecodable
fait le tour
extension Todo: JSONDecodable {
static func from(json json: JSONDictionary) -> Todo? {
guard let comment = json["comment"] as? String else { return nil }
return Todo(comment: comment)
}
}
Vous pouvez l'essayer avec ce code json:
{
"todos": [
{
"comment" : "The todo comment"
}
]
}
Lorsque vous l'avez obtenu à partir de l'API, vous pouvez le sérialiser comme les exemples précédents présentés dans une occurrence AnyObject
. Après cela, vous pouvez vérifier si l'instance est une instance de JSONDictionary
guard let jsonDictionary = dictionary as? JSONDictionary else { return }
L'autre chose à vérifier, spécifique à ce cas car vous avez un tableau de Todo
dans le JSON, est le dictionnaire todos
guard let todosDictionary = jsonDictionary["todos"] as? [JSONDictionary] else { return }
Maintenant que vous avez le tableau des dictionnaires, vous pouvez convertir chacun d'entre eux dans un objet Todo
en utilisant flatMap
(cela supprimera automatiquement les valeurs nil
du tableau).
let todos: [Todo] = todosDictionary.flatMap { Todo.from(json: $0) }
JSON Parsing Swift 3
Voici le fichier JSON que nous utiliserons appelé 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?"
} ] }
Importez votre fichier JSON dans votre projet
Vous pouvez effectuer cette fonction simple pour imprimer votre fichier 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)
}
}
}
}
Si vous voulez le placer dans une vue de table, je créerai d'abord un dictionnaire avec un NSObject.
Créez un nouveau fichier swift appelé ParsingObject
et créez vos variables de chaîne.
Assurez-vous que le nom de la variable est le même que le fichier JSON
. Par exemple, dans notre projet, nous avons un name
et une question
. Dans notre nouveau fichier rapide, nous utiliserons
var name: String?
var question: String?
Initialisez le NSObject que nous avons renvoyé dans notre tableau var ViewController.swift = ParsingObject Ensuite, nous effectuerions la même méthode que précédemment avec une modification mineure.
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)
}
}
Ensuite, nous le montrons dans notre tableview en faisant cela,
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
}