Swift, NSJSONSerialization и NSError

Проблема в том, что неполные данные NSJSONSerialization.JSONObjectWithData приводят к сбою приложения, дающего ошибку unexpectedly found nil while unwrapping an Optional value, вместо того, чтобы сообщать нам, используя переменную NSError. Поэтому мы не можем предотвратить крах.

Вы можете найти код, который мы используем ниже

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

До сих пор мы не можем найти работу.

Ответы

Ответ 1

Проблема заключается в том, что вы произвели результат десериализации JSON раньше проверка ошибки. Если данные JSON недействительны (например, неполные), то

NSJSONSerialization.JSONObjectWithData(...)

возвращает nil и

NSJSONSerialization.JSONObjectWithData(...) as NSDictionary

выйдет из строя.

Вот версия, которая корректно проверяет условия ошибки:

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

Примечания:

  • Правильный способ проверки ошибки - проверить возвращаемое значение, а не ошибка.
  • Функция чтения JSON .AllowFragments здесь не помогает. Установка этой опции разрешает только объекты верхнего уровня, которые не являются экземплярами NSArray или NSDictionary, например

    { "someString" }
    

Вы также можете сделать это в одной строке с дополнительным литьем as?:

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

Недостаток заключается в том, что в случае else вы не можете отличить, данные JSON не удались или если JSON не представлял словарь.

Ответ 2

Обновлен для 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)")
}

Ответ 3

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

Ответ 4

Вот расширение Swift 2, которое вы можете использовать для десериализации только 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".

Ответ 5

Swift 3 Пример NSJSONSerialization (чтение json из файла):

file data.json(пример отсюда: 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"
        }
    }
}
}
}

файл 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)")
            }

        }
    }
}

Результат (скриншот журнала)

введите описание изображения здесь