32151


パースについてお願いします。

"weather":[{"id":0,"main":"Rain","description":"light intensity shower
rain","icon":"09n"}]

をパースする場合、

[code]

    let task = URLSession.shared.dataTask(with: _url!,completionHandler:{data,response,error in do{
    
        let dict1:NSDictionary = try JSONSerialization.jsonObject(with: data!, options: JSONSerialization.ReadingOptions.mutableContainers) as! NSDictionary
    
[/code]

の後に、

[code]

    let dict = dict1["weather"] as! NSDictionary
    let id = dict2["id"] as! NSArray
    
[/code]

をすると、以下のエラーが出ます。

Could not cast value of type '__NSArrayM' to 'NSDictionary'

この解決方法はどのように記述すれば回避できますか？


CC BY-SA 3.0
2017-01-25T07:16:34.473
32144
2017-01-25T10:54:42.563
20049
question
swift json
jsonファイルのパースについて


あなたの示されたJSONデータで`"weather"`に対応する値は、`[...]`の形(必ず一番外側の記号に注目してください)になっています。これはJSON
object(読み込んだ後は`NSDictionary`)ではなく、JSON
array(読み込んだ後は`NSArray`)になります。(`__NSArrayM`というのは`NSArray`の一種です。)

そのエラーメッセージは

> (`NSArray`の一種である)`__NSArrayM`をキャストで`NSDictionary`に変換することはできません

と言っているわけです。

JSONデータの方に目を戻しますと、`[...]`の内側の要素は`{...}`の形、つまりJSON
objectが1個だけと言う状態です。あなたが扱いたいデータは、この内側のJSON objectでしょう。

つまり、「要素が1個だけ入っている配列からその要素を取り出す」と言うコードを書いてやる必要があります。

`as!`と言う演算子は「もしデータ型が間違っていたらアプリをクラッシュさせてください」と言うものですから、今回の例のようにデータ型に絶対の自信が持てない場所では使うべきではありません。

その辺を避けようとすると少々長くなりますが、例えばこんなコードになります:

[code]

    enum MyError: Error {
        case BadJSON(String)
    }
    
    //...
    
    do {
        guard let dict1 = try JSONSerialization.jsonObject(with: data!) as? [String: Any] else {
            throw MyError.BadJSON("Not a dictionary")
        }
        guard let arr = dict1["weather"] as? [[String: Any]] else {
            throw MyError.BadJSON("weather is not an array of dictionaries")
        }
        //`dict["weather"]`は配列なのでその中から最初の1個を使う
        guard let dict2 = arr.first else {
            throw MyError.BadJSON("weather is empty")
        }
        //"id"の値はJSON arrayではないので、`as! NSArray`としてはダメ
        guard let id = dict2["id"] as? Int else {
            throw MyError.BadJSON("cannot get id")
        }
        print(id) //->0
    } catch {
        print(error)
    }
    
[/code]

* * *

その他にも、

  * `NSArray`や`NSDictionary`ではなく、できるだけSwiftの`Array`や`Dictionary`を使った方が良い
  * 読み取った結果を変更することはないのだから、`JSONSerialization.ReadingOptions.mutableContainers`オプションは不要

と言う修正も同時に入れてしまったので、少しわかりにくい点があるかもしれませんが、何かありましたらコメント等でお知らせください。


CC BY-SA 3.0
2017-01-25T10:54:42.563
32151
2017-01-25T10:54:42.563
13972
32144
answer