

下記のソースに記述してあるURLだと、`NSJSONSerialization.JSONObjectWithData`で毎回止まってしまいます。

原因が全くわかりません。  
他のJSONのURLだと正常に動きます。  
どうすれば良いでしょうか。

[code]

    let url = NSURL(string: "http://api.syosetu.com/novelapi/api/?out=json&lim=1")!
    let task = NSURLSession.sharedSession().dataTaskWithURL(url,
        completionHandler: { data, response, error in
            if let str = NSString(data: data!, encoding: NSUTF8StringEncoding) {
                do {
                    let dict = try NSJSONSerialization.JSONObjectWithData(data!,
                            options: NSJSONReadingOptions.AllowFragments) as! NSDictionary
    
                } catch {}
            }
    })
    task.resume()
    
[/code]


CC BY-SA 3.0
2015-10-10T04:38:34.710
17494
2016-02-09T11:09:55.610
2015-10-10T04:46:54.390
5337
12703
question
swift json
NSJSONSerializationで止まってしまう


`JSONObjectWithData(_:options:)`のリファレンスを参照すると、返り値が`AnyObject`であることが分かると思います。その詳細については次のように説明されています。

> **Return Value**  
>  A Foundation object from the JSON data in data, or nil if an error occurs.
>
> [NSJSONSerialization Class
> Reference](https://developer.apple.com/library/mac/documentation/Foundation/Reference/NSJSONSerialization_Class/)

「JSONに応じて、Foundationのオブジェクトを返す」、とあります。実際的には、

  * ルート要素が辞書の場合、`NSDictionary`
  * ルート要素が配列の場合、`NSArray`

が返却されます。

ここでは`AnyObject`から`NSDictionary`へダウンキャスト（基底型から派生型への型変換、Obj-
Cクラスは全て`AnyObject`派生として扱われる）をしています。

ダウンキャストは安全ではないので、型が一致しない場合に実行時エラーとする`as!`と、`nil`として扱う`as?`のいずれかを用いる必要があります。ここでは前者を利用しているため実行時エラーとなります。

* * *

対応法はいろいろありますが、多分型チェックを行うのが一番確実です。

  1. 変換結果に対して型チェックを行い、配列であれば`Array`、辞書であれば`Dictionary`に変換する。
  2. JSONとして必ず辞書が来る想定であれば、`as!`でクラッシュするのは正当な動作です。または`as?`演算子を用いてダウンキャストして、`nil`チェックを行うこともできます。
  3. キー値コーディングを利用して、キーパスで要素にアクセスすれば、トップレベルオブジェクトの型は意識しなくても良いですが、型の考えに厳密なSwiftにおいては、あまり推奨されないと思います（ただ`NSJSONSerialization`そのものはこの利用法を想定しているような…）。


CC BY-SA 3.0
2015-10-12T08:57:11.770
17553
2015-10-12T08:57:11.770
5337
17494
answer