Le problème est quand il y a des données incomplètes NSJSONSerialization.JSONObjectWithData
plante l'application donnant unexpectedly found nil while unwrapping an Optional value
erreur au lieu de nous informer en utilisant la variable NSError. Nous ne pouvons donc pas empêcher un crash.
Vous pouvez trouver le code que nous utilisons ci-dessous
var error:NSError? = nil
let dataToUse = NSJSONSerialization.JSONObjectWithData(receivedData, options: NSJSONReadingOptions.AllowFragments, error:&error) as NSDictionary
if error != nil { println( "There was an error in NSJSONSerialization") }
Jusqu'à présent, nous ne pouvons pas trouver de solution.
Le problème est que vous convertissez le résultat de la désérialisation JSON avant de rechercher une erreur. Si les données JSON sont invalides (par exemple incomplètes),
NSJSONSerialization.JSONObjectWithData(...)
renvoie nil
et
NSJSONSerialization.JSONObjectWithData(...) as NSDictionary
va planter.
Voici une version qui vérifie correctement les conditions d'erreur:
var error:NSError? = nil
if let jsonObject: AnyObject = NSJSONSerialization.JSONObjectWithData(receivedData, options: nil, error:&error) {
if let dict = jsonObject as? NSDictionary {
println(dict)
} else {
println("not a dictionary")
}
} else {
println("Could not parse JSON: \(error!)")
}
Remarques:
L'option de lecture JSON .AllowFragments
n'aide pas ici. La définition de cette option autorise uniquement les objets de niveau supérieur qui ne sont pas une instance de NSArray
ou NSDictionary
, par exemple
{ "someString" }
Vous pouvez également le faire sur une seule ligne, avec un cast optionnel as?
:
if let dict = NSJSONSerialization.JSONObjectWithData(receivedData, options: nil, error:nil) as? NSDictionary {
println(dict)
} else {
println("Could not read JSON dictionary")
}
L'inconvénient est que dans le cas else
, vous ne pouvez pas distinguer si la lecture des données JSON a échoué ou si le JSON ne représentait pas un dictionnaire.
Pour une mise à jour de Swift 3, voir réponse de LightningStryk .
Mis à jour pour Swift 3
let jsonData = Data()
do {
let jsonObject = try JSONSerialization.jsonObject(with: jsonData, options:JSONSerialization.ReadingOptions(rawValue: 0))
guard let dictionary = jsonObject as? Dictionary<String, Any> else {
print("Not a Dictionary")
// put in function
return
}
print("JSON Dictionary! \(dictionary)")
}
catch let error as NSError {
print("Found an error - \(error)")
}
Swift 2
let JSONData = NSData()
do {
let JSON = try NSJSONSerialization.JSONObjectWithData(JSONData, options:NSJSONReadingOptions(rawValue: 0))
guard let JSONDictionary: NSDictionary = JSON as? NSDictionary else {
print("Not a Dictionary")
// put in function
return
}
print("JSONDictionary! \(JSONDictionary)")
}
catch let JSONError as NSError {
print("\(JSONError)")
}
Swift 3:
let jsonData = Data()
do {
guard let parsedResult = try JSONSerialization.jsonObject(with: jsonData, options: .allowFragments) as? NSDictionary else {
return
}
print("Parsed Result: \(parsedResult)")
} catch {
print("Error: \(error.localizedDescription)")
}
Voici une extension Swift 2 que vous pouvez utiliser pour désérialiser uniquement un NSDictionary:
extension NSJSONSerialization{
public class func dictionaryWithData(data: NSData, options opt: NSJSONReadingOptions) throws -> NSDictionary{
guard let d: NSDictionary = try self.JSONObjectWithData(data, options:opt) as? NSDictionary else{
throw NSError(domain: NSURLErrorDomain, code: NSURLErrorCannotParseResponse, userInfo: [NSLocalizedDescriptionKey : "not a dictionary"])
}
return d;
}
}
Désolé, je ne savais pas comment faire un retour de garde pour éviter de créer le "d" temporaire.
Exemple Swift 3 NSJSONSerialization (lire json à partir du fichier):
fichier data.json (exemple d'ici: http://json.org/example.html )
{
"glossary":{
"title":"example glossary",
"GlossDiv":{
"title":"S",
"GlossList":{
"GlossEntry":{
"ID":"SGML",
"SortAs":"SGML",
"GlossTerm":"Standard Generalized Markup Language",
"Acronym":"SGML",
"Abbrev":"ISO 8879:1986",
"GlossDef":{
"para":"A meta-markup language, used to create markup languages such as DocBook.",
"GlossSeeAlso":[
"GML",
"XML"
]
},
"GlossSee":"markup"
}
}
}
}
}
fichier JSONSerialization.Swift
extension JSONSerialization {
enum Errors: Error {
case NotDictionary
case NotJSONFormat
}
public class func dictionary(data: Data, options opt: JSONSerialization.ReadingOptions) throws -> NSDictionary {
do {
let JSON = try JSONSerialization.jsonObject(with: data , options:opt)
if let JSONDictionary = JSON as? NSDictionary {
return JSONDictionary
}
throw Errors.NotDictionary
}
catch {
throw Errors.NotJSONFormat
}
}
}
func readJsonFromFile() {
if let path = Bundle.main.path(forResource: "data", ofType: "json") {
if let data = NSData(contentsOfFile: path) as? Data {
do {
let dict = try JSONSerialization.dictionary(data: data, options: .allowFragments)
print(dict)
} catch let error {
print("\(error)")
}
}
}
}