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

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回答

798閲覧

swift:apiのレスポンスJSONをCodableで読み取りたい

TAKANOTAKUYA

総合スコア3

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/04 02:01

編集2021/05/04 12:17

swiftを学習して2週間、初学者です。

apiのレスポンスJSONをCodableで読み取りができないエラーに差し掛かっています。

エラー内容
typeMismatch(Swift.Array<Any>, Swift.DecodingError.Context(codingPath: [], debugDescription: "Expected to decode Array<Any> but found a dictionary instead.", underlyingError: nil))

導入したAPIはRakuten RapidAPIJokesというAPIを導入しています。

JSONテストで返ってくる値は

{2 items   "success":{1 item   "total":4 } "contents":{2 items   "categories":[4 items     0:{4 items       "language":"en"       "name":"jod"       "background":""       "description":"Joke of the day "     }     1:{4 items       "language":"en"       "name":"animal"       "background":""       "description":"Animal Joke of the day "     }     2:{4 items       "language":"en"       "name":"blonde"       "background":""       "description":"Blonde joke of the day!"     }     3:{4 items       "language":"en"       "name":"knock-knock"       "background":""       "description":"Knock Knock joke of the day!"     }   ] "copyright":"2019-20 https://jokes.one"   } }

この値のdescriptionのみ取得したいと考えています。

該当コード

import UIKit import Foundation struct Jokes: Codable { let contents: Contents enum CodingKeys: String, CodingKey { case contents = "contents" } } struct Contents: Codable { let categories: Categories enum CodingKeys: String, CodingKey { case categories = "categories" } } struct Categories: Codable { let description: String enum CodingKeys: String, CodingKey { case description = "description" } } class JokesViewController: UIViewController { private let cellId = "cellId" private var jokes = [Jokes]() let tableView: UITableView = { let tv = UITableView() return tv }() override func viewDidLoad() { super.viewDidLoad() view.addSubview(tableView) tableView.frame.size = view.frame.size tableView.backgroundColor = .black tableView.separatorColor = .lightGray tableView.separatorInset.left = 0 navigationItem.title = "元気がでる一言" tableView.delegate = self tableView.dataSource = self tableView.register( UITableViewCell.self, forCellReuseIdentifier: cellId) let headers = [ "x-rapidapi-key": "非表示にしてる", "x-rapidapi-host": "非表示にしてる" ] let request = NSMutableURLRequest(url: NSURL(string: "https://jokes.p.rapidapi.com/jod/categories")! as URL, cachePolicy: .useProtocolCachePolicy, timeoutInterval: 10.0) request.httpMethod = "GET" request.allHTTPHeaderFields = headers let session = URLSession.shared let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) in if (error != nil) { print("error: ",error) } else { let httpResponse = response as? HTTPURLResponse print("httpResponse: ",httpResponse) } if let data = data { do { let joke = try JSONDecoder().decode([Jokes].self, from: data) self.jokes = joke DispatchQueue.main.async { self.tableView.reloadData() } print("json: ", joke) } catch(let error) { print("情報の取得に失敗しました。:", error) } } }) dataTask.resume() } } extension JokesViewController: UITableViewDelegate, UITableViewDataSource { func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat { return 180 } func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return jokes.count } func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = UITableViewCell(style: UITableViewCell.CellStyle.subtitle, reuseIdentifier: "cellId") print("jokes: " ,jokes) // cell.textLabel?.text = jokes[indexPath.row] .description cell.textLabel?.textColor = .white cell.textLabel?.numberOfLines = 0 cell.backgroundColor = .black cell.selectionStyle = UITableViewCell.SelectionStyle.none cell.translatesAutoresizingMaskIntoConstraints = false return cell } }

上記のコードで実装を行っています。

実際に試したこと

レスポンスの取得の際に下記の書き方が悪いのかと思い、[]をなくしてみたり、

if let data = data { do { let joke = try JSONDecoder().decode([Jokes].self, from: data) self.jokes = joke DispatchQueue.main.async { self.tableView.reloadData() } print("json: ", joke) } catch(let error) { print("情報の取得に失敗しました。:", error) } }

Codableの部分に

struct Jokes: Codable { let contents: Contents enum CodingKeys: String, CodingKey { case contents = "contents" } } struct Contents: Codable { let categories: Categories enum CodingKeys: String, CodingKey { case categories = "categories" } } struct Categories: Codable { let description: String enum CodingKeys: String, CodingKey { case description = "description" } }

?を入れたりして実装を行いましたが、一向にJSONをCodableで読み取りたいができませんでした。

もしお手すかれましたら、ご返信いただけましたら幸いです。

何卒宜しくお願いします。

自身での修正箇所

Codableの部分がネストしていることを見落としており、修正を加えました。

struct Jokes: Codable { let contents: [Contents] struct Contents: Codable { let categories: [Categories] struct Categories: Codable { let description: String } } }

修正を加えたのですが、しかしながら現状は情報を取得まで至っていません????

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

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

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

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

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

tomato879241

2021/05/04 06:16

上に書かれているのはJSON形式のデータではないですけど?
TAKANOTAKUYA

2021/05/04 07:38

そうなんですね。 すみません、JSON形式だと思っていました。 内容としては、APIの情報を取得したいと言う内容だったのですが、質問内容が間違っており申し訳ありません。
TAKANOTAKUYA

2021/05/04 08:12

質問内容修正させて頂きました。 お手すかれた際にで大丈夫ですので、宜しくお願い致します。
hoshi-takanori

2021/05/04 14:37

JSON には 2 items とか 0: みたいなのは出てこないはずですが、それはさておき、let contents: [Contents] は配列ではないのでは…。 また、余計なお世話とは思いますが、struct Categories は一つのカテゴリーなので Category という名前 (単数形) にして、その配列は let categories: [Category] とするのが良いかと。
TAKANOTAKUYA

2021/05/04 15:47

ご回答ありがとうございます。 おっしゃる通りcontentsは配列ではなかったです。 [Category] のご指摘もありがとうございます。 確かに単数系の記述の方が良いですね。 修正を加えました。 ``` struct Jokes: Codable { let contents: Contents //修正箇所 []無くす、配列ではないため struct Contents: Codable { let categories: [Category] //修正箇所 単数系に変更 struct Category: Codable { //修正箇所 単数系に変更 let description: String } } } ``` 上記のように修正行いましたが、やはり未だ情報取得ができておりませんでした。 ``` if let data = data { do { let joke = try JSONDecoder().decode([Jokes].self, from: data) self.jokes = joke DispatchQueue.main.async { self.tableView.reloadData() } print("json: ", joke) } catch(let error) { print("情報の取得に失敗しました。:", error) } } ``` 情報の取得の書き方がおかしいのでしょうか?
hoshi-takanori

2021/05/04 15:52

> let joke = try JSONDecoder().decode([Jokes].self, from: data) ↑ [Jokes].self だとトップレベルに Jokes の配列があることになりますが、実際には多分一つしかないと思うので、Jokes.self では。
TAKANOTAKUYA

2021/05/04 16:07

そう言うことですね。 早急のご回答誠にありがとうございます。 確かに配列は一つしかなかったです。 変更すると無事情報取得ができました。 ありがとうございます。 しかしながら、selfで記述した文が割り当てできないエラーと遭遇しました。 let joke = try JSONDecoder().decode(Jokes.self, from: data) > self.jokes = joke //変更すると左記でエラーが出ました エラー内容 Cannot assign value of type 'Jokes' to type '[Jokes]' 日本語訳 タイプ「ジョーク」の値をタイプ「[ジョーク]」に割り当てることはできません 受け取った値を表示する際のコードでも cell.textLabel?.text = jokes[indexPath.row] .description Value of type 'Jokes' has no member 'description'のエラーが続きます。 上記に関しても二日がかりで行なっているのですが、なぜかが突き止められなく もし可能でしたらアドバイス頂けましたら幸いです。 度々申し訳ありません。
TAKANOTAKUYA

2021/05/04 16:33

> cell.textLabel?.text = jokes[indexPath.row] .description 上記に関しては修正行い cell.textLabel?.text = jokes[indexPath.row] .contents.categories.description 無事エラーが解決できました。 しかし、データは取得できていて、descriptionだけ画面に表示しようとしているのですが、表示がされません。 やはり > self.jokes = joke が原因箇所なのでしょうか?
hoshi-takanori

2021/05/04 16:52

self.jokes は [Jokes] なので、Jokes ひとつだけ代入することはできなくて、配列にする必要があります。 が、それ以前に、JokesViewController でリスト表示したいのは struct Jokes でも struct Contents でもなく、struct Category の配列なのでは? (またはカテゴリー選択後に実際にそのカテゴリーの joke を取得して表示したいのでは…。)
TAKANOTAKUYA

2021/05/04 22:42

おっしゃる通りで、struct Category配列の中のdescriptionのみ、画面が表示したら取得したい情報でした。 特に選択等はなく画面が出たらdescriptionのみ表示するだけです。 そもそもが間違っていたのですね。 ご指摘いただきありがとうございます。 そのため self.jokes = joke //変更前 self.jokes = Category["description"] //変更後 Category の配列の中のdescriptionを取得したいと言う意味で上記に変更したのですが、やはりエラーがでており、書き方が間違っているのでしょうか? 他の書き方も行なっているのですが、なぜダメなのかがわからなかったです。 度々申し訳ないですが、ご教授いただけましたら幸いです。
TAKANOTAKUYA

2021/05/04 23:33

Category の配列の中のdescriptionを表示したいと言うところで private var jokes = [Jokes]() //変更前 private var jokes = [Category]() // 変更後 変更したものはいいもののエラーが続いてしまっています汗
hoshi-takanori

2021/05/04 23:54

プロパティの定義を private var jokes = [Category]() // 変更後 と変更した上で、 self.jokes = joke.contents.categories とすれば良いかと。
TAKANOTAKUYA

2021/05/05 00:20

ご連絡いただきありがとうございます。 self.jokes = joke.contents.categories に変更しました。 .でつないで指定すると言うことですね。[]は必要なかったのが引っ掛かっていた原因でした。 上記書き換えさせていただいたのですが、再度エラーが出ており、 Cannot assign value of type '[Jokes.Contents.Category]' to type '[Category]' (aka 'Array<OpaquePointer>') 日本語訳 タイプ '[Jokes.Contents.Category]'の値をタイプ '[Category]'に割り当てることはできません(別名 'Array <OpaquePointer>') このエラーでは、'[Category]'に割り当てることはできないと言うことなのでdescriptionno 値を別の箇所に代入しなくては行けないと言うことでしょうか? 度々すいません。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

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

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

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

ただいまの回答率
85.35%

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

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

質問する

関連した質問