ぴよログ

↓に移転したのでこっちは更新されません、多分。

入れ子になったNSDictionaryのデータを取り出す

移転しました →

何重にも入れ子になったNSDictionaryのデータをチマチマ取り出すのは超絶めんどくさいです。valueForKeyで取り出したNSDictionaryにさらにvalueForKeyして、さらに、、なんてやるともうやってられません。

幸いvalueForKeyPathというメソッドで少し簡単にできるので、そちらを使ったほうがいいと思われます。

例によってYoutubeAPIの戻り値をNSDictionaryにしたものを対象とします。*1

データはこれ。

{
 "kind": "youtube#searchListResponse",
 "etag": "\"qQvmwbutd8GSt4eS4lhnzoWBZs0/TvUA3aT9bg7gzx72UgdpWtsjAD0\"",
 "nextPageToken": "CAUQAA",
 "pageInfo": {
  "totalResults": 1000000,
  "resultsPerPage": 5
 },
 "items": [
  {
   "kind": "youtube#searchResult",
   "etag": "\"qQvmwbutd8GSt4eS4lhnzoWBZs0/YNAKTLX6SbYCco99EQWCyVDIxCo\"",
   "id": {
    "kind": "youtube#video",
    "videoId": "lwYgdBQ0k3U"
   },
   "snippet": {
    "publishedAt": "2013-10-04T05:16:29.000Z",
    "channelId": "UCZfWmiGEbwWNPMe2nr3WdDg",
    "title": "【WOWOW】U-12ジュニアサッカーワールドチャレンジ2013 FCバルセロナ ダイジェスト",
    "description": "U-12ジュニアサッカーワールドチャレンジ2013 FCバルセロナ ダイジェスト ☆U-12 Junior soccer world challenge 2013 FC Barcerona Infantil B digest 2013年8月27 ...",
    "thumbnails": {
     "default": {
      "url": "https://i.ytimg.com/vi/lwYgdBQ0k3U/default.jpg"
     },
     "medium": {
      "url": "https://i.ytimg.com/vi/lwYgdBQ0k3U/mqdefault.jpg"
     },
     "high": {
      "url": "https://i.ytimg.com/vi/lwYgdBQ0k3U/hqdefault.jpg"
     }
    },
    "channelTitle": "WOWOWofficial",
    "liveBroadcastContent": "none"
   }
  },
  {
    // ...
  }
 ]
} 

このjsonがNSDictionaryに対応しているとします。データには動画情報が複数含まれているので次のコードのようにループで回しながら処理することを基本とします。

NSArray* items = (NSArray*)[dic valueForKey:@"items"];

for (NSDictionary* item in items) {
    // process an item
} 

そして、各itemの標準サムネイルを取り出したいとします。階層で言うと、snippetthumbnailsdefaulturlです。

これを馬鹿正直にやろうとすると、、、

NSDictionary* snippet = [item valueForKey:@"snippet"];
NSDictionary* thumbnails = [snippet valueForKey:@"thumbnails"]; 
NSDictionary* default = [thumbails valueForKey:@"default"]; 
NSString* url = [default valueForKey:@"url"]; 

となります。まあアホらしいですね。

ところがvalueForKeyPathを使うと、、、

NSString* url = [item valueForKeyPath:@"snippet.thumbnails.default.url"];

一行で書ける!

これまでObjective-Cでこういった入れ子のデータを扱って来なかったので初めて知りました。きっと基本なんでしょうね。

注意点

Objective-C - ネストしたJSONデータのプロパティアクセスにはvalueForKeyPath:が便利 - Qiita [キータ]

こちらのリンク先でninjinkun氏がこのように述べていました。もっともだなあ。一応気をつけよう。

この方法、便利ではありますが以下の理由で仕事ではあまり使わないようにしています。

  • コンパイラに頼れないので変更に弱い
    • テストがあればOKです
  • データ構造をそのまま引き回す羽目になるので見通しが悪い
    • きちんとモデルクラスを作ってプロパティを定義した方が他人が見てもわかりやすい

多少面倒でもモデルを作った方が何かと良いというのが今のところの結論です。個人プロダクト及びCoreDataを使う場合には多用しています。