swift nsjsonserialization nserror

Swift, NSJSONSerialization y NSError



(5)

El problema es cuando hay datos incompletos. NSJSONSerialization.JSONObjectWithData está fallando en la aplicación, lo que da un resultado unexpectedly found nil while unwrapping an Optional value error de unexpectedly found nil while unwrapping an Optional value lugar de informarnos que usamos la variable NSError. Así que somos incapaces de evitar el choque.

Puede encontrar el código que estamos usando a continuación.

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") }

Hasta ahora no podemos encontrar un trabajo alrededor.


Actualizado para 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)") }


Aquí hay una extensión de Swift 2 que puede usar para deserializar solo 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; } }

Lo siento, no estaba seguro de cómo hacer una devolución de guardia para evitar crear la ''d'' temporal.


El problema es que emite el resultado de la deserialización JSON antes de comprobar si hay un error. Si los datos JSON no son válidos (por ejemplo, incompletos), entonces

NSJSONSerialization.JSONObjectWithData(...)

devuelve nil y

NSJSONSerialization.JSONObjectWithData(...) as NSDictionary

se estrellará

Aquí hay una versión que verifica las condiciones de error correctamente:

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!)") }

Observaciones:

  • La forma correcta de verificar si hay un error es probar el valor de retorno , no la variable de error.
  • La opción de lectura JSON .AllowFragments no ayuda aquí. La configuración de esta opción solo permite que los objetos de nivel superior que no son una instancia de NSArray o NSDictionary , por ejemplo

    { "someString" }

También puede hacerlo en una línea, con un lanzamiento opcional as? :

if let dict = NSJSONSerialization.JSONObjectWithData(receivedData, options: nil, error:nil) as? NSDictionary { println(dict) } else { println("Could not read JSON dictionary") }

La desventaja es que, en el caso else , no puede distinguir si la lectura de los datos JSON falló o si JSON no representó un diccionario.

Para una actualización a Swift 3, vea la respuesta de LightningStryk .


Swift 3 NSJSONSerialization sample (lea json del archivo):

archivo data.json (ejemplo desde aquí: 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" } } } } }

archivo 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 } } }

Uso

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)") } } } }

Resultado (log screenshot)


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)") }