質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.35%
JSON

JSON(JavaScript Object Notation)は軽量なデータ記述言語の1つである。構文はJavaScriptをベースとしていますが、JavaScriptに限定されたものではなく、様々なソフトウェアやプログラミング言語間におけるデータの受け渡しが行えるように設計されています。

パース

パースとは、一定の文法に従って記述されたテキスト文書を解析し、データ構造の集合体に分解・変換することを呼びます。

Swift

Swiftは、アップルのiOSおよびOS Xのためのプログラミング言語で、Objective-CやObjective-C++と共存することが意図されています

検索

検索は、あるデータの集まりの中から 目的のデータを見つけ出すことです。

API

APIはApplication Programming Interfaceの略です。APIはプログラムにリクエストされるサービスがどのように動作するかを、デベロッパーが定めたものです。

Q&A

0回答

834閲覧

【JSONのパースがうまくいかない】APIを使ったゴルフ場検索アプリを作りたい

urota

総合スコア0

JSON

JSON(JavaScript Object Notation)は軽量なデータ記述言語の1つである。構文はJavaScriptをベースとしていますが、JavaScriptに限定されたものではなく、様々なソフトウェアやプログラミング言語間におけるデータの受け渡しが行えるように設計されています。

パース

パースとは、一定の文法に従って記述されたテキスト文書を解析し、データ構造の集合体に分解・変換することを呼びます。

Swift

Swiftは、アップルのiOSおよびOS Xのためのプログラミング言語で、Objective-CやObjective-C++と共存することが意図されています

検索

検索は、あるデータの集まりの中から 目的のデータを見つけ出すことです。

API

APIはApplication Programming Interfaceの略です。APIはプログラムにリクエストされるサービスがどのように動作するかを、デベロッパーが定めたものです。

0グッド

0クリップ

投稿2021/05/30 13:38

前提・実現したいこと

・Swift5を使用。
・ゴルフ場のキーワードをSearch Textに入力し、「楽天GORAゴルフ場検索API」のパラメータから、情報を抽出し、予め指定したゴルフ場の情報のみTableViewに表示させたい。

【参考】楽天ゴルフ場検索API詳細URL
https://webservice.rakuten.co.jp/api/goragolfcoursesearch/

発生している問題・エラーメッセージ

・エディタ上にエラーメッセージは表示されず、ビルドも通りますが、ゴルフ場のキーワードをSearch Textに入力し検索しても、TableViewに何も表示されない。(オチる事もないです)

該当のソースコード

Swift5

1import UIKit 2import SafariServices 3 4class ViewController: UIViewController,UISearchBarDelegate,UITableViewDataSource,UITableViewDelegate, SFSafariViewControllerDelegate { 5 6 override func viewDidLoad() { 7 super.viewDidLoad() 8 // Do any additional setup after loading the view. 9 10 //search Barのdelegate通知先を設定 11 searchText.delegate = self 12 13 //ブレースホルダー(searchText欄に表示される)を設定 14 searchText.placeholder = "ゴルフ場の名前を入力" 15 16 //TableViewのdataSourceを設定 17 tableView.dataSource = self 18 19 //TableViewのdelegateを設定 20 tableView.delegate = self 21 22 } 23 24 //story boardのsearch barと紐付け 25 @IBOutlet weak var searchText: UISearchBar! 26 27 //story boardのTableViewと紐付け 28 @IBOutlet weak var tableView: UITableView! 29 30 //ゴルフのリスト(タプル配列) 31 var golfiList : [(golfCourseName:String ,golfCourseCaption:String , golfCourseDetailUrl:URL , golfCourseImageUrl:URL)] = [] 32 33 //検索ボタンを押下時の実行ファンクション 34 func searchBarSearchButtonClicked(_ searchBar: UISearchBar) { 35 36 //キーボードを閉じる 37 view.endEditing(true) 38 39 //入力されたテキストを取り出す 40 if let searchWord = searchBar.text { 41 42 //入力されていたら(nilでない場合)ゴルフ場を検索 43 searchGolf(keyword: searchWord) 44 45 } 46 47 } 48 49 //JSONのitem内のデータを構造 50 struct ItemJson: Codable { 51 52 //ゴルフ場の名称 53 let golfCourseName: String? 54 55 //メーカー 56 let golfCourseCaption: String? 57 58 //掲載URL 59 let golfCourseDetailUrl: URL? 60 61 //画像URL 62 let golfCourseImageUrl: URL? 63 64 } 65 66 //JSONのデータ構造 67 struct ResultJson: Codable { 68 69 //複数要素 70 let item:[ItemJson]? 71 72 } 73 74 //searchGolfファンクション 75 //第一引数:keyword 検索したいワード 76 func searchGolf(keyword : String) { 77 78 //ゴルフ場の検索キーワードをAPIが認識できるようURLにエンコードする 79 guard let keyword_encode = keyword.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) 80 81 else { 82 83 return 84 85 } 86 87 //リクエストURLの組み立て 88 guard let req_url = URL(string: "https://app.rakuten.co.jp/services/api/Gora/GoraGolfCourseSearch/20170623?format=json&keyword=(keyword_encode)&applicationId= 〜省略〜") 89 90 else { 91 92 return 93 94 } 95 96 print(req_url) 97 98 //リクエストに必要な情報を生成 99 let req = URLRequest(url: req_url) 100 101 //データ転送を管理するためのセッションを生成 102 let session = URLSession(configuration: .default, delegate: nil,delegateQueue: OperationQueue.main) 103 104 //リクエストタスクとして登録 105 let task = session.dataTask(with: req, completionHandler: { 106 107 (data , respons , error) in 108 109 //セッションを終了 110 session.finishTasksAndInvalidate() 111 112 //do try catch エラーハンドリング 113 do { 114 115 //JSONDecoderのインスタンス取得 116 let decoder = JSONDecoder() 117 118 //受け取ったJSONデータをパース(解析)して格納 119 let json = try decoder.decode(ResultJson.self, from: data!) 120 121 //ゴルフ場の情報が取得出来ているか確認 122 if let items = json.item { 123 124 //printでここから流れない☆☆☆☆☆☆☆☆ 125 126 //ゴルフ場のリストを初期化 127 self.golfiList.removeAll() 128 129 //取得しているゴルフ場の数だけ処理 130 for item in items { 131 132 //ゴルフ場の名称、メーカー名、掲載URL、画像URLをアンラップ 133 if let golfCourseName = item.golfCourseName , let golfCourseCaption = item.golfCourseCaption , let golfCourseDetailUrl = item.golfCourseDetailUrl , let golfCourseImageUrl = item.golfCourseImageUrl { 134 135 //1つのゴルフ場をタプルでまとめて管理 136 let golf = (golfCourseName,golfCourseCaption,golfCourseDetailUrl,golfCourseImageUrl) 137 138 //ゴルフ場の配列へ追加 139 self.golfiList.append(golf) 140 141 } 142 143 } 144 145 //Table Viewを更新する 146 self.tableView.reloadData() 147 148 if let golfdbg = self.golfiList.first { 149 150 print ("----------------") 151 print ("golfiList[0] = (golfdbg)") 152 153 } 154 155 } 156 157 } catch { 158 159 //エラー処理 160 print("エラーが出ました") 161 162 } 163 164 }) 165 166 //ダウンロード開始 167 task.resume() 168 169 } 170 171 //セルの個数を設定するファンクション 172 func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { 173 174 //検索にヒットした分だけセルを生成したいので、ゴルフリストのカウントで指定。 175 return golfiList.count 176 177 } 178 179 //セルの値を設定するファンクション 180 func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { 181 182 //今回表示を行うCellオブジェクト(1行)を取得する 183 let cell = tableView.dequeueReusableCell(withIdentifier: "golfCell") 184 185 //ゴルフ場のタイトル設定 186 cell?.textLabel?.text = golfiList[indexPath.row].golfCourseName 187 188 //ゴルフ場画像を取得 189 if let imageData = try? Data(contentsOf: golfiList[indexPath.row].golfCourseImageUrl) { 190 191 //正常に取得できた場合は、UIImageで画像オブジェクトを生成して、セルにゴルフ場画像を設定 192 cell?.imageView?.image = UIImage(data: imageData) 193 194 } 195 196 //設定済みのCellオブジェクトを画面に反映 197 return cell! 198 199 } 200 201 func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { 202 203 //ハイライト解除 204 tableView.deselectRow(at: indexPath, animated: true) 205 206 //SFSafariViewを開く 207 let safariViewController = SFSafariViewController(url: golfiList[indexPath.row].golfCourseDetailUrl) 208 209 //delegateの通知先を自分自身に 210 safariViewController.delegate = self 211 212 //SafariViewが開かれる 213 present(safariViewController, animated: true, completion: nil) 214 215 } 216 217 //SafariViewが閉じられた時に呼ばれるdelegateメソッド 218 func safariViewControllerDidFinish(_ controller: SFSafariViewController) { 219 220 //SafariViewを閉じる 221 dismiss(animated: true, completion: nil) 222 223 } 224 225}

試したこと

printでデバックを行ったところ、下記の箇所で読込みが止まっていることがわかりました。

//ゴルフ場の情報が取得出来ているか確認 if let items = json.item { //printでここから流れない☆☆☆☆☆☆☆☆ //ゴルフ場のリストを初期化 self.golfiList.removeAll()

また、JSONのレスポンスを確認したところ、下記のとおり「items」の中で「item」が使われていることが分かりました。

"count": 86, "page": 1, "first": 1, "last": 30, "hits": 30, "carrier": 0, "pageCount": 3, "Items": [ { "Item": { "golfCourseId": 80004, "golfCourseName": "アジア取手カントリー倶楽部", "golfCourseAbbr": "アジア取手CC", "golfCourseNameKana": "あじあとりでかんとりーくらぶ", "golfCourseCaption": "常磐道の谷和原I.C.より15分!都心からのアクセスの良さが魅力。姉妹コースに続き、【セグウェイ】を100台導入!日本一の導入数を誇ります。フェアウェイ乗り入れ可能で移動も快適、【セグウェイでゴルフ】を是非体験してみて下さい。\n\n【お得プランの一部をご紹介】\n・全日廻り放題!*当日の天候、日没状況により、追加ラウンドできない場合もございます。\n・お誕生月特典あり!\n・平日は食べ放題のランチバイキング、4サム割引きあり。\nお得で楽しい一日をお気軽にお過ごし下さい。", "address": "茨城県取手市稲1340", "latitude": 35.9061664, "longitude": 140.0397556, "highway": "常磐自動車道谷和原", "golfCourseDetailUrl": "https://booking.gora.golf.rakuten.co.jp/guide/disp/c_id/80004", "reserveCalUrl": "https://search.gora.golf.rakuten.co.jp/cal/disp/c_id/80004", "ratingUrl": "https://booking.gora.golf.rakuten.co.jp/voice/detail/c_id/80004", "golfCourseImageUrl": "https://gora.golf.rakuten.co.jp/img/golf/80004/photo1.jpg", "evaluation": 3.6 } }, 〜以下略〜

そのため、下記の箇所のどこかでパースがうまくいっていないと考えているのですが、解決策が見つかりません。

//JSONのitem内のデータを構造 struct ItemJson: Codable { //ゴルフ場の名称 let golfCourseName: String? //メーカー let golfCourseCaption: String? //掲載URL let golfCourseDetailUrl: URL? //画像URL let golfCourseImageUrl: URL? } //JSONのデータ構造 struct ResultJson: Codable { //複数要素 let item:[ItemJson]? }

補足情報(FW/ツールのバージョンなど)

使用ツール
Xcode(Version 12.4)

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

hoshi-takanori

2021/05/30 18:45

ResultJson のプロパティは item じゃなくて Items ですね。そして、Items は Item というプロパティを一つだけ持ったオブジェクトの配列なので、それを表す構造体が必要です。
urota

2021/06/04 13:02

ご回答ありがとうございます。 ご指摘いただいた部分を調べてみます。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

まだ回答がついていません

会員登録して回答してみよう

アカウントをお持ちの方は

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.35%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問