J'essaie de récupérer une réponse JSON et de stocker les résultats dans une variable. Des versions de ce code fonctionnaient dans les versions précédentes de Swift, jusqu'à la sortie de la version GM de Xcode 8. J'ai jeté un œil à quelques articles similaires sur StackOverflow: Swift 2 Parsing JSON - Impossible d'indiquer une valeur de type 'AnyObject' et JSON Parsing dans Swift 3 .
Cependant, il semble que les idées qui y sont véhiculées ne s'appliquent pas dans ce scénario.
Comment analyser correctement la réponse JSON dans Swift 3? Quelque chose a-t-il changé dans la façon dont JSON est lu dans Swift 3?
Voici le code en question (il peut être exécuté dans une aire de jeux):
import Cocoa
let url = "https://api.forecast.io/forecast/apiKey/37.5673776,122.048951"
if let url = NSURL(string: url) {
if let data = try? Data(contentsOf: url as URL) {
do {
let parsedData = try JSONSerialization.jsonObject(with: data as Data, options: .allowFragments)
//Store response in NSDictionary for easy access
let dict = parsedData as? NSDictionary
let currentConditions = "\(dict!["currently"]!)"
//This produces an error, Type 'Any' has no subscript members
let currentTemperatureF = ("\(dict!["currently"]!["temperature"]!!)" as NSString).doubleValue
//Display all current conditions from API
print(currentConditions)
//Output the current temperature in Fahrenheit
print(currentTemperatureF)
}
//else throw an error detailing what went wrong
catch let error as NSError {
print("Details of JSON parsing error:\n \(error)")
}
}
}
Edit: Voici un exemple des résultats de l'appel API aprèsprint(currentConditions)
["icon": partly-cloudy-night, "precipProbability": 0, "pressure": 1015.39, "humidity": 0.75, "precipIntensity": 0, "windSpeed": 6.04, "summary": Partly Cloudy, "ozone": 321.13, "temperature": 49.45, "dewPoint": 41.75, "apparentTemperature": 47, "windBearing": 332, "cloudCover": 0.28, "time": 1480846460]
Réponses:
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
.se produit parce que le compilateur n'a aucune idée du type des objets intermédiaires (par exemple
currently
dans["currently"]!["temperature"]
) et que vous utilisez des types de collection Foundation commeNSDictionary
le 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
URLSession
et exclusivement des types natifs SwiftPour imprimer toutes les paires clé / valeur,
currentConditions
vous pouvez écrireUne note concernant
jsonObject(with data
:De nombreux (il semble tout) des tutoriels suggèrent
.mutableContainers
ou des.mutableLeaves
options qui est tout à fait non - sens dans Swift. Les deux options sont des options Objective-C héritées pour affecter le résultat auxNSMutable...
objets. Dans Swift, toutvar
iable est mutable par défaut et le fait de passer l'une de ces options et d'assigner le résultat à unelet
constante 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
.allowFragments
qui est nécessaire si si l'objet racine JSON pourrait être un type de valeur (String
,Number
,Bool
ounull
) au lieu de l' un des types de collecte (array
oudictionary
). Mais normalement, omettez leoptions
paramè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
[]
- Swift:[Any]
mais dans la plupart des cas[[String:Any]]
{}
- Swift:[String:Any]
Les types de valeur sont
"Foo"
, paire"123"
ou"false"
- Swift:String
123
ou123.0
- Swift:Int
ouDouble
true
oufalse
pas entre guillemets - Swift:true
oufalse
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]
et récupérez les valeurs par clés avec (
OneOfSupportedJSONTypes
est une collection JSON ou un type de valeur comme décrit ci-dessus.)Si l'objet racine est un tableau (
[]
) transtypez le type en[[String:Any]]
et parcourez le tableau avec
Si vous avez besoin d'un élément à un index spécifique, vérifiez également si l'index existe
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'
.allowFragments
option et convertir le résultat dans le type de valeur approprié, par exempleApple a publié un article complet dans le blog Swift: Travailler avec JSON dans Swift
=================================================== ==========================
Dans Swift 4+, le
Codable
protocole 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é)
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:URL
peuvent être décodées directement commeURL
.time
entier peut être décodé commeDate
avec ledateDecodingStrategy
.secondsSince1970
.keyDecodingStrategy
.convertFromSnakeCase
Autres sources codables:
la source
dict!["currently"]!
dans un dictionnaire que le compilateur peut inférer en toute sécurité l'abonnement de clé suivant.Un grand changement qui s'est produit avec Xcode 8 Beta 6 pour Swift 3 était que l'id importe désormais en tant que
Any
plutôt queAnyObject
.Cela signifie qu'il
parsedData
est renvoyé sous forme de dictionnaire le plus probable avec le type[Any:Any]
. Sans utiliser de débogueur, je ne pourrais pas vous dire exactement ce queNSDictionary
fera votre distribution , mais l'erreur que vous voyez est parce que ledict!["currently"]!
typeAny
Alors, comment résolvez-vous cela? D'après la façon dont vous l'avez référencé, je suppose qu'il
dict!["currently"]!
s'agit d'un dictionnaire et que vous avez donc de nombreuses options:Tout d'abord, vous pouvez faire quelque chose comme ceci:
Cela vous donnera un objet dictionnaire que vous pourrez ensuite interroger pour les valeurs et ainsi vous pourrez obtenir votre température comme ceci:
Ou si vous préférez, vous pouvez le faire en ligne:
J'espère que cela aide, j'ai bien peur de ne pas avoir eu le temps d'écrire un exemple d'application pour le tester.
Une dernière remarque: la chose la plus simple à faire est peut-être de simplement convertir la charge utile JSON
[String: AnyObject]
dès le début.la source
dict!["currently"]! as! [String: String]
va planter[String: String]
ne peut pas fonctionner du tout car il y a quelques valeurs numériques. Cela ne fonctionne pas dans un Mac Playgroundla source
J'ai construit quicktype exactement dans ce but. Collez simplement votre exemple de JSON et quicktype génère cette hiérarchie de types pour vos données API:
Il génère également un code de marshaling sans dépendance pour coaxer la valeur de retour de
JSONSerialization.jsonObject
dans aForecast
, y compris un constructeur pratique qui prend une chaîne JSON afin que vous puissiez rapidement analyser uneForecast
valeur fortement typée et accéder à ses champs:Vous pouvez installer quicktype à partir de npm avec
npm i -g quicktype
ou utiliser l'interface utilisateur Web pour obtenir le code généré complet à coller dans votre terrain de jeu.la source
Mis à jour par la
isConnectToNetwork-Function
suite, grâce à ce post .J'ai écrit une méthode supplémentaire pour cela:
Alors maintenant, vous pouvez facilement appeler cela dans votre application où vous le souhaitez
la source
Swift a une puissante inférence de type. Permet de se débarrasser du passe-partout "si laissé" ou "garde laisse" et forcer le déroulement en utilisant une approche fonctionnelle:
Juste une ligne de code et aucun déballage forcé ou casting de type manuel. Ce code fonctionne dans le terrain de jeu, vous pouvez donc le copier et le vérifier. Voici une implémentation sur GitHub.
la source
C'est une autre façon de résoudre votre problème. Veuillez donc vérifier la solution ci-dessous. J'espère que cela vous aidera.
la source
Le problème vient de la méthode d'interaction API. L'analyse JSON n'est modifiée que dans la syntaxe. Le principal problème concerne la manière de récupérer les données. Ce que vous utilisez est un moyen synchrone d'obtenir des données. Cela ne fonctionne pas dans tous les cas. Ce que vous devriez utiliser, c'est un moyen asynchrone de récupérer des données. De cette façon, vous devez demander des données via l'API et attendre qu'elle réponde avec des données. Vous pouvez y parvenir avec une session URL et des bibliothèques tierces comme
Alamofire
. Vous trouverez ci-dessous le code de la méthode de session URL.la source
Si je crée un modèle en utilisant json précédent En utilisant ce lien [blog]: http://www.jsoncafe.com pour générer une structure codable ou tout format
Modèle
Analyser
la source