🎄teratailクリスマスプレゼントキャンペーン2024🎄』開催中!

\teratail特別グッズやAmazonギフトカード最大2,000円分が当たる!/

詳細はこちら
Swift Playground

Swift Playgroundは、Swiftをインタラクティブに習得できるiPad向けのアプリケーション。コーディングの知識は一切必要なく、Swift Playgrounds上でプログラミングしたコードによりドローン・ロボットを自在に動かすことが可能です。

JSON

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

Xcode

Xcodeはソフトウェア開発のための、Appleの統合開発環境です。Mac OSXに付随するかたちで配布されています。

Swift

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

Q&A

1回答

2217閲覧

ネストされたJSONをデコードする方法について教えてください

youto

総合スコア4

Swift Playground

Swift Playgroundは、Swiftをインタラクティブに習得できるiPad向けのアプリケーション。コーディングの知識は一切必要なく、Swift Playgrounds上でプログラミングしたコードによりドローン・ロボットを自在に動かすことが可能です。

JSON

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

Xcode

Xcodeはソフトウェア開発のための、Appleの統合開発環境です。Mac OSXに付随するかたちで配布されています。

Swift

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

0グッド

0クリップ

投稿2019/12/05 06:19

前提・実現したいこと

プログラミング初心者です。
SwiftUIでネストされたJSONファイルをリスト化するシステムを作っています。
PlaygroundsでJSONファイルをデコードする機能を実装中に以下のエラーメッセージが発生しました。
解決方法を教えていただけないでしょうか。

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

Error decoding JSON: typeMismatch(Swift.Array<Any>, Swift.DecodingError.Context(codingPath: [], debugDescription: "Expected to decode Array<Any> but found a dictionary instead.", underlyingError: nil))

該当のソースコード

Swift

1import SwiftUI 2import PlaygroundSupport 3 4let todosEndpoint = "ここにJSONファイルのURLを記載しました。(内容は下記の「JSON」に記載しています。)" 5 6struct Todo: Codable, Identifiable { 7 let id :UUID 8 let t1,t2,t3: Times 9 10 struct Times:Codable, Identifiable { 11 let id: String 12 let name : String 13 } 14} 15 16typealias Todos = [Todo] 17 18class TodoDownloader: ObservableObject { 19 @Published var todos: Todos = [Todo]() 20 init() { 21 guard let url = URL(string: todosEndpoint) else { return } 22 URLSession.shared.dataTask(with: url) { (data, response, error) in 23 do { 24 guard let data = data else { return } 25 let todos = try JSONDecoder().decode(Todos.self, from: data) 26 DispatchQueue.main.async { 27 self.todos = todos 28 } 29 } catch { 30 print("Error decoding JSON: ", error) 31 } 32 }.resume() 33 } 34} 35 36struct contentView: View { 37 @ObservedObject var todoData: TodoDownloader = TodoDownloader() 38 39 var body: some View { 40 NavigationView { 41 List(self.todoData.todos) { todo in 42 Text(todo.t1.name) 43 } 44 .navigationBarTitle(Text("To Do List")) 45 } 46 } 47} 48 49#if DEBUG 50struct contentView_Previews: PreviewProvider { 51 static var previews: some View { 52 contentView() 53 } 54} 55#endif 56 57PlaygroundPage.current.liveView = UIHostingController(rootView: contentView())

JSON

1{"t1":{"id":"001","name":"hoge1"}, 2"t2":{"id":"","name":""}, 3"t3":{"id":"003","name":"hoge2"},

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

Xcode11.2.1,SwiftUI

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

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

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

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

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

guest

回答1

0

Todos.selfと書いていますが、そのJSONは配列ではありません。
また、Todo.idに相当するものがJSON内にありません。

デコード時にUUIDを生成する

swift

1struct Todo: Codable, Identifiable { 2 3 //追加分だけ 4 private enum CodingKeys: String, CodingKey { 5 case t1, t2, t3 6 } 7 8 init(from decoder: Decoder) throws { 9 let values = try decoder.container(keyedBy: CodingKeys.self) 10 id = UUID() 11 t1 = try values.decode(Times.self, forKey: .t1) 12 t2 = try values.decode(Times.self, forKey: .t2) 13 t3 = try values.decode(Times.self, forKey: .t3) 14 } 15}

投稿2019/12/05 06:37

編集2019/12/05 08:39
fuzzball

総合スコア16733

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

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

fuzzball

2019/12/05 06:41

let id :UUIDは、たぶんString。
youto

2019/12/05 06:46

ご回答していただきありがとうございます。 一度JSONを配列にする必要があるのでしょうか。 let id :UUIDとIdentifiableを消してしまうと、List(self.todoData.todos) { todo inで下記のエラ〜メッセージがでてきてしまうため、追記してしまいました。 Initializer 'init(_:rowContent:)' requires that 'Todo' conform to 'Identifiable' JSON内にIDがない場合はどのように記載すればよろしかったのでしょうか。 基本的な質問ばかりになってしまい申し訳ございません。
fuzzball

2019/12/05 06:57

IdentifiableはXcode11から追加されたものなんですね。 ちょっと再確認します。
youto

2019/12/05 07:03

申し訳ございません。 よろしくお願いいたします。
fuzzball

2019/12/05 07:59

>>一度JSONを配列にする必要があるのでしょうか。 まずこれから解決しますが、質問内のJSONは、受信したJSONの一部を切り取ったものでしょうか? 配列として送られてきているなら、[ ] で囲われているはずです。 細かいことを言うと、質問内のJSONはフォーマットが正しくありません。 ・最後の , が不要 ・最後の } が無い
fuzzball

2019/12/05 08:35

あと、もう見ないかも知れないので先に書いておくと、UUIDは、Todo に init(from:) を実装して、その中で生成しないといけないと思います。この辺はCodableの勉強して下さい。(一応、コードを書いておきます)
youto

2019/12/05 08:54

ご連絡が遅くなってしまい申し訳ございません。 JSONファイルは一部を切り取ったもので、フォーマットが正しくないのは私の切り取りミスです。 []では囲われてはおりませんでした。
youto

2019/12/05 08:55

わかりました。 Codableについて調べてみます。
fuzzball

2019/12/18 02:48 編集

>>[]では囲われてはおりませんでした。 ということはJSONが配列になっていないということですから、まずはそこから確認しないとダメですね。 一つしか無いデータを配列にするなら(すごく適当に書くと)、 let todo = try JSONDecoder().decode(Todo.self, from: data) self.todos = [todo] //配列にする こんな感じになるかと。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

まだベストアンサーが選ばれていません

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

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

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

ただいまの回答率
85.36%

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

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

質問する

関連した質問