Tout d'abord ne jamais charger les données de façon synchrone à partir d'une URL distante, utilisez toujours des méthodes asynchrones comme URLSession
.
"Tout" n'a pas d'indice membres
se produit parce que le compilateur n'a aucune idée de ce type les objets intermédiaires sont (par exemple, currently
en ["currently"]!["temperature"]
) et puisque vous êtes à l'aide de la Fondation de la collecte des types comme NSDictionary
le compilateur n'a pas d'idée sur le type.
En outre, dans Swift 3 il est tenu d'en informer le compilateur sur le type de tous les indicée objets.
Vous devez lancer le résultat de la sérialisation JSON pour le type réel.
Ce code utilise URLSession
et exclusivement Swift types natifs
let urlString = "https://api.forecast.io/forecast/apiKey/37.5673776,122.048951"
let url = URL(string: urlString)
URLSession.shared.dataTask(with:url!) { (data, response, error) in
if error != nil {
print(error)
} else {
do {
let parsedData = try JSONSerialization.jsonObject(with: data!) as! [String:Any]
let currentConditions = parsedData["currently"] as! [String:Any]
print(currentConditions)
let currentTemperatureF = currentConditions["temperature"] as! Double
print(currentTemperatureF)
} catch let error as NSError {
print(error)
}
}
}.resume()
Pour imprimer toutes les paires clé / valeur de currentConditions
vous pouvez écrire
let currentConditions = parsedData["currently"] as! [String:Any]
for (key, value) in currentConditions {
print("\(key) - \(value) ")
}
Une remarque concernant l' jsonObject(with data
:
De nombreux (il semble) tutoriels suggèrent .mutableContainers
ou .mutableLeaves
options qui est complètement absurde en Swift. Les deux options sont hérités Objective-C d'affecter le résultat d' NSMutable...
objets. Dans Swift tout var
iable est mutable par défaut et en passant de l'une de ces options et en assignant le résultat d'un let
de la constante n'a pas d'effet du tout. En outre, la plupart des implémentations ne sont jamais la mutation de la désérialisé JSON de toute façon.
Le seul (rare) option qui est utile dans la Swift est - .allowFragments
ce qui est nécessaire si si le JSON de la racine de l'objet peut être un type de la valeur(String
, Number
, Bool
ou null
) plutôt qu'un des types de collection (array
ou dictionary
). Mais normalement omettre l' options
paramètre qui signifie Pas d'options.
===========================================================================
Quelques considérations d'ordre général pour parser JSON
JSON est un bien agencé, le format de texte. Il est très facile de lire une chaîne JSON. Lire la chaîne soigneusement. Il y a seulement six types différents – deux types de collection et de quatre types de valeur.
Les types de collection sont
-
Tableau - JSON: les objets entre crochets
[]
- Swift: [Any]
, mais dans la plupart des cas [[String:Any]]
-
Dictionnaire - JSON: les objets dans des accolades
{}
- Swift: [String:Any]
Les types de valeur sont
-
Chaîne - JSON: toute valeur entre guillemets doubles
"Foo"
, même "123"
ou "false"
– Swift: String
-
Nombre - JSON: les valeurs numériques sont pas des guillemets,
123
ou 123.0
– Swift: Int
ou Double
-
Bool - JSON:
true
ou false
pas de guillemets doubles – Swift: true
ou false
-
null - JSON:
null
– Swift: NSNull
Selon le JSON spécification de toutes les clés dans les dictionnaires doivent être String
.
Fondamentalement, il est toujours recommandé d'utiliser en option pour les liaisons de déballer les options en toute sécurité
Si l'objet racine est un dictionnaire ({}
) en fonte le type d' [String:Any]
if let parsedData = try JSONSerialization.jsonObject(with: data!) as? [String:Any] { ...
et de récupérer les valeurs par touches (OneOfSupportedJSONTypes
est soit JSON de la collection ou de la valeur type décrit ci-dessus.)
if let foo = parsedData["foo"] as? OneOfSupportedJSONTypes {
print(foo)
}
Si la racine de l'objet est un tableau ([]
) en fonte le type d' [[String:Any]]
if let parsedData = try JSONSerialization.jsonObject(with: data!) as? [[String:Any]] { ...
et de parcourir le tableau avec
for item in parsedData {
print(item)
}
Si vous avez besoin d'un élément à l'indice de vérifier également si l'index n'existe
if let parsedData = try JSONSerialization.jsonObject(with: data!) as? [[String:Any]], parsedData.count > 2,
let item = parsedData[2] as? OneOfSupportedJSONTypes {
print(item)
}
}
Dans les rares cas où le JSON est tout simplement l'un des types de valeur – plutôt que d'un type de collection – vous devrez passer à l' .allowFragments
option et jeté le résultat à la valeur appropriée, par exemple de type
if let parsedData = try JSONSerialization.jsonObject(with: data!, options: .allowFragments) as? String { ...
Apple a publié un article dans la Swift Blog: Travailler avec JSON en Swift
Mise à jour: Swift 4+ Codable
protocole fournit un moyen plus pratique pour parser JSON directement dans les structures / classes.