Tout d'abord, ne chargez jamais les données de manière synchrone à partir d'une URL distante , utilisez toujours des méthodes asynchrones comme URLSession.
'Any' n'a pas de membres en indice
se produit parce que le compilateur n'a aucune idée du type des objets intermédiaires (par exemple currentlydans ["currently"]!["temperature"]) et que vous utilisez des types de collection Foundation comme NSDictionaryle compilateur n'a aucune idée du type.
De plus, dans Swift 3, il est nécessaire d'informer le compilateur du type de tous les objets en indice.
Vous devez convertir le résultat de la sérialisation JSON en type réel.
Ce code utilise URLSessionet exclusivement des types natifs Swift
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, currentConditionsvous pouvez écrire
let currentConditions = parsedData["currently"] as! [String:Any]
for (key, value) in currentConditions {
print("\(key) - \(value) ")
}
Une note concernant jsonObject(with data:
De nombreux (il semble tout) des tutoriels suggèrent .mutableContainersou des .mutableLeavesoptions qui est tout à fait non - sens dans Swift. Les deux options sont des options Objective-C héritées pour affecter le résultat aux NSMutable...objets. Dans Swift, tout variable est mutable par défaut et le fait de passer l'une de ces options et d'assigner le résultat à une letconstante n'a aucun effet. De plus, la plupart des implémentations ne font jamais muter le JSON désérialisé de toute façon.
La seule option (rare) qui est utile dans Swift est ce .allowFragmentsqui est nécessaire si si l'objet racine JSON pourrait être un type de valeur ( String, Number, Boolou null) au lieu de l' un des types de collecte ( arrayou dictionary). Mais normalement, omettez le optionsparamètre qui signifie Aucune option .
=================================================== ==========================
Quelques considérations générales pour analyser JSON
JSON est un format de texte bien organisé. Il est très facile de lire une chaîne JSON. Lisez attentivement la chaîne . Il n'y a que six types différents: deux types de collection et quatre types de valeur.
Les types de collection sont
- Array - JSON: objets entre crochets
[]- Swift: [Any]mais dans la plupart des cas[[String:Any]]
- Dictionnaire - JSON: objets entre accolades
{}- Swift:[String:Any]
Les types de valeur sont
- Chaîne - JSON: toute valeur entre guillemets
"Foo", paire "123"ou "false"- Swift:String
- Number - JSON: valeurs numériques non entre guillemets
123ou 123.0- Swift: IntouDouble
- Bool - JSON:
trueou false pas entre guillemets - Swift: trueoufalse
- null - JSON:
null- Swift:NSNull
Selon la spécification JSON, toutes les clés des dictionnaires doivent être String.
En gros, il est toujours recommandé d'utiliser des liaisons optionnelles pour dérouler les options en toute sécurité
Si l'objet racine est un dictionnaire ( {}), transtypez le type en[String:Any]
if let parsedData = try JSONSerialization.jsonObject(with: data!) as? [String:Any] { ...
et récupérez les valeurs par clés avec ( OneOfSupportedJSONTypesest une collection JSON ou un type de valeur comme décrit ci-dessus.)
if let foo = parsedData["foo"] as? OneOfSupportedJSONTypes {
print(foo)
}
Si l'objet racine est un tableau ( []) transtypez le type en[[String:Any]]
if let parsedData = try JSONSerialization.jsonObject(with: data!) as? [[String:Any]] { ...
et parcourez le tableau avec
for item in parsedData {
print(item)
}
Si vous avez besoin d'un élément à un index spécifique, vérifiez également si l'index existe
if let parsedData = try JSONSerialization.jsonObject(with: data!) as? [[String:Any]], parsedData.count > 2,
let item = parsedData[2] as? OneOfSupportedJSONTypes {
print(item)
}
}
Dans le cas rare où le JSON est simplement l'un des types de valeur - plutôt qu'un type de collection - vous devez passer l' .allowFragmentsoption et convertir le résultat dans le type de valeur approprié, par exemple
if let parsedData = try JSONSerialization.jsonObject(with: data!, options: .allowFragments) as? String { ...
Apple a publié un article complet dans le blog Swift: Travailler avec JSON dans Swift
=================================================== ==========================
Dans Swift 4+, le Codableprotocole fournit un moyen plus pratique d'analyser JSON directement en structures / classes.
Par exemple, l'exemple JSON donné dans la question (légèrement modifié)
let jsonString = """
{"icon": "partly-cloudy-night", "precipProbability": 0, "pressure": 1015.39, "humidity": 0.75, "precip_intensity": 0, "wind_speed": 6.04, "summary": "Partly Cloudy", "ozone": 321.13, "temperature": 49.45, "dew_point": 41.75, "apparent_temperature": 47, "wind_bearing": 332, "cloud_cover": 0.28, "time": 1480846460}
"""
peut être décodé dans la structure Weather. Les types Swift sont les mêmes que ceux décrits ci-dessus. Il existe quelques options supplémentaires:
- Les chaînes représentant un
URLpeuvent être décodées directement comme URL.
- L'
timeentier peut être décodé comme Dateavec le dateDecodingStrategy .secondsSince1970.
- Les clés JSON snaked_cased peuvent être converties en camelCase avec
keyDecodingStrategy .convertFromSnakeCase
struct Weather: Decodable {
let icon, summary: String
let pressure: Double, humidity, windSpeed : Double
let ozone, temperature, dewPoint, cloudCover: Double
let precipProbability, precipIntensity, apparentTemperature, windBearing : Int
let time: Date
}
let data = Data(jsonString.utf8)
do {
let decoder = JSONDecoder()
decoder.dateDecodingStrategy = .secondsSince1970
decoder.keyDecodingStrategy = .convertFromSnakeCase
let result = try decoder.decode(Weather.self, from: data)
print(result)
} catch {
print(error)
}
Autres sources codables: