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

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

新規登録して質問してみよう
ただいま回答率
85.48%
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

解決済

1回答

1737閲覧

Json 取得する型が異なる場合について

ko-ru

総合スコア27

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クリップ

投稿2020/02/24 09:39

編集2020/02/24 11:09

前提・実現したいこと

SwiftでぐるなびAPIを利用しています。
https://api.gnavi.co.jp/api/manual/restsearch/
ぐるなびAPIからJSONを利用してデータを取得していますが、下記エラーが出ます。

The data couldn’t be read because it isn’t in the correct format.

Jsonで取得したデータが空の場合の挙動についてまだ理解していないため定かではありませんが、ぐるなびAPIのbudgetの値がある場合はINTとして取得しており、値が空の場合" "になっていることから、Stringで取得してしまっているように見えます。

JSON

1 { 2 "@attributes": { 3 "api_version": "v3" 4 }, 5 "total_hit_count": 20832, 6 "hit_per_page": 10, 7 "page_offset": 1, 8 "rest": [ 9 { 10 "@attributes": { 11 "order": 0 12 }, 13 "id": "", 14 "update_date": "", 15 "name": "", 16 "name_kana": "", 17 "latitude": "", 18 "longitude": "", 19 "category": "", 20 "url": "", 21 "url_mobile": "", 22 "coupon_url": { 23 "pc": "", 24 "mobile": "" 25 }, 26 "image_url": { 27 "shop_image1": "", 28 "shop_image2": "", 29 "qrcode": "" 30 }, 31 "address": "", 32 "tel": "", 33 "tel_sub": "", 34 "fax": "", 35 "opentime": "", 36 "holiday": "", 37 "access": { 38 "line": "", 39 "station": "", 40 "station_exit": "", 41 "walk": "", 42 "note": "" 43 }, 44 "parking_lots": "", 45 "pr": { 46 "pr_short": "", 47 "pr_long": "" 48 }, 49 "code": { 50 "areacode": "", 51 "areaname": "", 52 "prefcode": "", 53 "prefname": "", 54 "areacode_s": "", 55 "areaname_s": "", 56 "category_code_l": [ 57 "", 58 "" 59 ], 60 "category_name_l": [ 61 "", 62 "" 63 ], 64 "category_code_s": [ 65 "", 66 "" 67 ], 68 "category_name_s": [ 69 "", 70 "" 71 ] 72 }, 73 "budget": 3000, //値がない場合は、"budget": "", 74 "party": 4000, 75 "lunch": 980, 76 "credit_card": "", 77 "e_money": "", 78 "flags": { 79 "mobile_site": 1, 80 "mobile_coupon": 1, 81 "pc_coupon": 1 82 } 83 }, 84

Swift

1 // MARK: - REST 2 struct REST: Codable { 3 let id: String 4 let budget:Int 5 6 enum CodingKeys: String, CodingKey { 7 case id = "id" 8 case budget = "budget" 9 } 10 }

Stringで定義してしまうと、値があるときにINTとなってしまうためエラーで出てしまい、
Intで定義してしまうと、値がない時にStringとなってしまいエラーが出てしまうため、どうすれば良いかわからない状況です。

またコード自体の書き方に問題がある可能性もあるため、解決方法をご教授いただけば幸いです。

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

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

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

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

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

hoshi-takanori

2020/02/24 10:49

情報が少なくて分かりません。「Stringで取得してしまっているように見えます」って、あなたがそういうコードを書いてるのでは?
ko-ru

2020/02/24 11:11

情報が少ないということで失礼しました。 コード等を追加いたしましたのでミス等があれば教えていただきたいです。
guest

回答1

0

ベストアンサー

とりあえずこれでどうでしょう?

swift

1enum StrOr<T: Codable>: Codable { 2 case str(String) 3 case val(T) 4 5 init(from decoder: Decoder) throws { 6 let container = try decoder.singleValueContainer() 7 if let value = try? container.decode(String.self) { 8 self = .str(value) 9 } else { 10 let value = try container.decode(T.self) 11 self = .val(value) 12 } 13 } 14 15 func encode(to encoder: Encoder) throws { 16 var container = encoder.singleValueContainer() 17 switch (self) { 18 case .str(let value): 19 try container.encode(value) 20 case .val(let value): 21 try container.encode(value) 22 } 23 } 24} 25 26struct REST: Codable { 27 let id: String 28 let budget: StrOr<Int> 29}

テストコード

swift

1func testCodable() { 2 testDecode("{ \"id\": \"abc\", \"budget\": 123 }") 3 testDecode("{ \"id\": \"abc\", \"budget\": \"123\" }") 4 5 testEncode(REST(id: "abc", budget: .val(123))) 6 testEncode(REST(id: "abc", budget: .str("123"))) 7} 8 9func testDecode(_ json: String) { 10 do { 11 let decoder = JSONDecoder() 12 let rest = try decoder.decode(REST.self, from: json.data(using: .utf8)!) 13 print("REST: id = (rest.id), budget = (rest.budget)") 14 } catch let error { 15 print(error) 16 } 17} 18 19func testEncode(_ rest: REST) { 20 do { 21 let encoder = JSONEncoder() 22 encoder.outputFormatting = .prettyPrinted 23 let data = try encoder.encode(rest) 24 print(String(data: data, encoding: .utf8)!) 25 } catch let error { 26 print(error) 27 } 28}

実行結果

REST: id = abc, budget = val(123) REST: id = abc, budget = str("123") { "id" : "abc", "budget" : 123 } { "id" : "abc", "budget" : "123" }

追記。StrOr<Int> の値を取り出すには switch を使って以下のように書きます。

swift

1var rest: REST = ... // 何らかの値を取得したと仮定します。 2 3switch rest.budget { 4case .str(let strValue): // strValue という変数名は自由に変えて構いません。 5 // 文字列の場合。strValue を使って処理を行う。 6case .val(let value): 7 // Int の場合。value を使って処理を行う。 8}

参考: Swiftのenumについてまとめてみる - Qiita

投稿2020/02/25 17:25

編集2020/02/27 21:13
hoshi-takanori

総合スコア7895

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

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

ko-ru

2020/02/27 14:16

ありがとうございます。 回答していただいた方法で取得することができました! そこで1点質問なのですが、独自で作成した型(StrOr<Int>)をStringに変換することは可能でしょうか? 教えていただけると幸いです。
ko-ru

2020/02/28 12:38

取得から値の取り出しまでできました! どうしてもエラーが出てしまって困っていたので助かりました。 独自に型を宣言して、処理を書いて値を取り出すときはSwitchを使用してStringかIntかに分ける。勉強なりました。ありがとうございました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問