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

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

ただいまの
回答率

88.61%

Swift4 JSONデータをCodableで格納する方法

解決済

回答 2

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 1,421

tomotomo104114

score 15

🔰Swift4 JSONデータをCodableで格納する方法

はじめて質問させていただきます。

以下のJSONデータをstrctに格納したいのですが、ネストされたデータを構造体に取得できませんでした。

頂いた回答と参考書を参考にコードを修正した所、nemeのJSONデータは取得できましたが、locationデータを取得できませんでした。

locationの中のデータを格納する際にエラーとなってしまう原因がわかりません。

参考書(たった2日でマスターできるiPhoneアプリ開発集中講座 Xcode 10 Swift 4.2対応 より)

参考書では全体を表す構造は作成していなかったので、今回はその形式を参考にしています。

どうぞよろしくおねがいします。

[JSONデータ]

{
    {
    "meta": {
        "code": 200,
        "requestId": "5c7f2a5b1ed2196e46ea83ef"
    },
    "response": {
        "venues": [
            {
                "id": "4b792714f964a52018ed2ee3",
                "name": "東海道新幹線 東京駅",
                "contact": {},
                "location": {
                    "address": "丸の内1-9-1",
                    "lat": 35.681154862661515,
                    "lng": 139.7679060569781,
                    "distance": 71,
                    "postalCode": "100-0005",
                    "cc": "JP",
                    "city": "千代田区",
                    "state": "東京都",
                    "country": "日本",
                    "formattedAddress": [
                        "丸の内1-9-1",
                        "千代田区, 東京都",
                        "100-0005",
                        "日本"
                    ]
                },
                "categories": [
                    {
                        "id": "4bf58dd8d48988d129951735",
                        "name": "鉄道駅",
                        "pluralName": "鉄道駅",
                        "shortName": "鉄道駅",
                        "icon": {
                            "prefix": "https://ss3.4sqi.net/img/categories_v2/travel/trainstation_",
                            "suffix": ".png"
                        },
                        "primary": true
                    }
                ],
                "verified": false,
                "stats": {
                    "tipCount": 0,
                    "usersCount": 0,
                    "checkinsCount": 0,
                    "visitsCount": 0
                },
               略
            }
        ],
        "confident": false
    }
}
[現在のコード]

import UIKit
import FoursquareAPIClient
import Social

class SettingViewController: UIViewController, UISearchBarDelegate { //, UITableViewDataSource,UITableViewDelegate {

    override func viewDidLoad() {
        super.viewDidLoad()

        // Do any additional setup after loading the view.

        searchVenue.delegate = self

        searchVenue.placeholder = "検索スポットの入力"

       // tableView.dataSource = self

        // tableView.delegate = self
    }


    @IBOutlet weak var searchVenue: UISearchBar!
    @IBOutlet weak var tableView: UITableView!

    //venueListをタプル配列で宣言 必要な部分のみを格納して配列に追加するためにここで宣言
    var venueList : [(name:String, contact:String, city:String, state : String, country:String)] = []

    func searchBarSearchButtonClicked(_ searchBar: UISearchBar) {
        view.endEditing(true)

        if let searchWord = searchBar.text {
        print(searchWord)
        }
    }

    //取得したレスポンスデータ(JSON)を記録する構造体を宣言

    //ItemJson構造体に以下のレスポンスデータを格納
    struct ItemJson : Codable { //Codable : JSONを変換にした時に、一括して変数にデータを格納するプロトコル
        let response : JsonResponse
    }

        struct JsonResponse : Codable {
            let venues : [JsonVenues]
        }

            struct JsonVenues : Codable {
                let name     : String?
            }
            //↑ここまでは狙い通りの結果


            /* ↓ここをコメントアウトすると格納時エラーになります

            //===================================================


                        let location : [Jsonlocation]

    }

                                struct Jsonlocation : Codable {
                                    let city    : String?
                                    let state   : String?
                                    let country : String?
                             }
                //===================================================


        */


    struct ResultJson : Codable {
        let item : [ItemJson]?
    }


    //現在位置ボタンタップ時リクエストURL作成
    @IBAction func searchNearVenue(_ sender: Any) {


        //認証KeyであるIDとSecretの宣言
        let clientId = "*****"
        let clientSecret = "*****"

        // 現在位置を東京駅に指定
        // to do GPSから現在位置を取得する
        let currentLocation_latitude    = 35.681236
        let currentLocation_longitude   = 139.767125

        //緯度経度からvenue検索するreq_URL作成。検索数、範囲を指定。


        guard let req_url = URL (string : "https://api.foursquare.com/v2/venues/search?v=20180228&ll=\(currentLocation_latitude),\(currentLocation_longitude)&intent=checkin&limit=1&radius=4000&client_id=\(clientId)&client_secret=\(clientSecret)")
            else {
            return
        }

        //req_URLのデバック
        print(req_url)

        //以下で、req_urlにアクセスし構造体にレスポンスデータを格納する処理を行う

        //req_urlをロードするプロトコルを宣言
        let req = URLRequest(url: req_url)

        let session = URLSession(configuration: .default, delegate: nil, delegateQueue: OperationQueue.main)

        //ダウンロード先URL:req、data格納、通信状態データ格納、エラー内容格納
        let task = session.dataTask(with: req, completionHandler: {
            (data, response, error) in

            session.finishTasksAndInvalidate()

            // パースの際にエラーになった場合の例外処理でdo catch
            do {

                //JSONをデコードするためのオブジェクトを作成
                let decoder = JSONDecoder()

                //受け取ったJSONデータをパースして格納

                //↓decode(***)が鍵である
                //ResultJsonだとエラーItemJsonにしたらなぜか上手くいった


                let json = try decoder.decode(ItemJson.self, from: data!)

                //デバック用
                print (json)


            } catch {

            print("エラーが発生しました")

            }

        })

        task.resume()

    }



}   //end
参考書が使っているJSONデータ

{
    "item": [
        {
            "id": "6833",
            "name": "ベビースターチップス(CoCo壱番屋カツカレー味)",
            "kana": "べびーすたーちっぷすここいちばんやかつかれーあじ",
            "maker": "おやつカンパニー",
            "price": "128",
            "type": "snack",
            "regist": "2010年7月2日",
            "url": "略/",
            "tags": {
                "tag": [
                    "カレー",
                    "コラボ",
                    "ベビースター",
                    "季節限定"
                ]
            },
            "image": "http://www.sysbird.jp/toriko/wp-content/blogs.dir/2/files/6833.gif",
            "comment": "<p>略</p>\n"
//以下略
        },
        </p>\n"
        }
    ],
    "status": "OK",
    "count": "2"
}
参考書のコード

 //Jsonのitem内のデータ構造
    struct ItemJson: Codable {
        //お菓子の名前
        let name: String?
        //メーカー
        let maker: String?
        //掲載URL
        let url: URL?
        //画像のURL
        let image: URL?
    }

    //JSONのデータ構造
    struct ResultJson: Codable {
        //複数要素
        let item:[ItemJson]?
    }

    //searchokashiメソッド
    func searchOkashi (keyword: String) {

        //お菓子の検索キーワードをURLエンコードする
        guard let keyword_encode = keyword.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) else {
            return
        }

        //リクエストURLの組み立て
        guard let req_url = URL(string:
            "http://www.sysbird.jp/toriko/api/?apikey=guest&format=json&keyword=\(keyword_encode)&max=10&prder=r") else {
                return
        }
        print(req_url)

        //リクエストに必要な情報を生成
        let req = URLRequest(url: req_url)
        //データ転送を管理するためのセッションを生成
        let session = URLSession(configuration: .default, delegate: nil, delegateQueue: OperationQueue.main)
        //リクエストをタスクとして登録
        let task = session.dataTask(with: req, completionHandler: {
            (data, response, error) in
            //セッション終了
            session.finishTasksAndInvalidate()
            //do try catcyエラーハンドリング
            do{
                //JSONDecoderのインスタンス取得
                let decoder = JSONDecoder()
                //受け取ったJSONデータをパース(解析)して格納
                let json = try decoder.decode(ResultJson.self, from:data!)


                //お菓子の情報が取得できているか確認
                if let items = json.item {
                    //お菓子のリストを初期化
                    self.okashiList.removeAll()
                    //取得したお菓子の数だけ処理
                    for item in items {
                        //お菓子の名前、メーカ名、掲載URL,画像URLをアンラップ
                        if let name = item.name, let maker = item.maker, let link = item.url, let image = item.image {
                            //1つのお菓子をタプルでまとめて管理
                            let okashi = (name, maker, link, image)
                            //お菓子の配列へ追加
                            self.okashiList.append(okashi)
                        }
                    }
                    //TableView を更新する
                    self.tableView.reloadData()
                    if let okashidbg = self.okashiList.first {
                        print ("-----------------")
                        print ("okashiList[0] = \(okashidbg)")
                    }
                }

            } catch {
                //エラー処理
                print("エラーが出ました")
            }
        })
        //ダウンロード開始
        task.resume()
    }
//以下略
    }
  • 気になる質問をクリップする

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

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

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 2

+2

JSONDecoderはJSONの一部だけをデコードすることは出来ません。
JSON全体を表すstructを作ってください。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2019/03/05 06:55 編集

    お返事ありがとうございます!

    以下のようにresponse全体を表すstruct構造を試してみましたが、格納する際にエラーとなりました。
    どこが間違ってしまったのでしょうか?どうぞよろしくお願いします。

    struct ItemJson : Codable {

    var response : ItemJsonResponse

    struct ItemJsonResponse : Codable {

    var venues : [ItemJsonVenues]

    }

    struct ItemJsonVenues : Codable {


    let name : String?
    let city : String?
    let state : String?
    let country : String?
    }

    }

    struct ResultJson : Codable {

    let response : [ItemJson]?
    }

    キャンセル

  • 2019/03/05 10:13

    metaがありません。
    responseは配列ではありません。
    categoriesがありません。

    全体を表すstructを作ってください。

    キャンセル

  • 2019/03/06 11:39 編集

    MasakiHori様
    度々質問をしてしまい申し訳ございませんが、質問を編集致しましたのでお時間のある時に確認していただけると幸いです。
    現在nameの格納はできましたが、その他のデータが格納できません。
    参考書では全体を表すstructを作成していなかったので、必要な部分のみ正確にstructできれば良いのかな?と思いコードを修正しました。
    今回はたまたまnameは取得できたましたが、MasakiHoriさんのおっしゃるように全体structを作成していないことでlocation内のデータが取得できなかったのかなとも思っています

    キャンセル

check解決した方法

0

自己解決しました
JSONデータの表記方法を調べて理解したら簡単な話でした。

 //ItemJson構造体に以下のレスポンスデータを格納
        struct ItemJson : Codable { 
            let response : JsonResponse
        }

            struct JsonResponse : Codable {
                let venues : [JsonVenues]
            }

                struct JsonVenues : Codable {
                    let name     : String?

                    //JSONデータがオブジェクトだったのに配列で宣言していたので格納エラーになっていた
                    let location : Jsonlocation
                }

                    struct Jsonlocation : Codable {
                        let city    : String?
                        let state   : String?
                        let country : String?
                    }

        // structの配列化
        struct ResultJson : Codable {
            let item : [ItemJson]?
        }

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

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

  • ただいまの回答率 88.61%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る