web-dev-qa-db-ja.com

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

今まで、私たちは回避策を見つけることができません。

17
Hope

問題は、エラーをチェックする前にJSONデシリアライゼーションbeforeの結果をキャストすることです。 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" }
    

オプションのcastas?

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

欠点は、elseの場合、JSONデータの読み取りが失敗したか、JSONが辞書を表していないかを区別できないことです。

Swift 3の更新については、 LightningStrykの答え を参照してください。

24
Martin R

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

スイフト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)")
}
46
LightningStryk

スウィフト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)")
}
1
Ashok R

NSDictionaryのみを逆シリアル化するために使用できるSwift 2拡張機能は次のとおりです。

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」の作成を避けるためにガードリターンを行う方法がわかりませんでした。

0
malhal

Swift 3 NSJSONSerializationサンプル(ファイルからjsonを読み取ります):

ファイル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)")
            }

        }
    }
}

結果(ログスクリーンショット)

enter image description here

0