下記のように店舗情報のキーが数字になっているJSONデータをデシリアライズできるか?と質問があり回答しました。
{ "response": { "total_hit_count": 4, "0": { "shop_id": "6072772", "shop_name": "店舗A" }, "1": { "shop_id": "6072773", "shop_name": "店舗B" }, "2": { "shop_id": "6072774", "shop_name": "店舗C" }, "3": { "shop_id": "6072775", "shop_name": "店舗D" } } }
そもそもとして店舗情報は配列で欲しい……と気持ちがあります。ぐるなびAPIらしいです。
解決編
CodingKeyを継承したキーリストは固定値(enum)である必要はないので、下記のように定義することができます。
struct CodingKeys: CodingKey { var stringValue: String init?(stringValue: String) { self.stringValue = stringValue } var intValue: Int? init?(intValue: Int) { return nil } }
コードの全体としては下記の通りです。このAPIの数字部分がどこまで伸びるか仕様を把握できていないので、仮に 0 〜 total_hit_count までの数字のキーがあると想定しています。
import Foundation struct Reviews: Decodable { let response: RakutenResponse } struct RakutenResponse: Decodable { let total_hit_count: Int let reviews: [Review] // MARK: - codable struct CodingKeys: CodingKey { var stringValue: String init?(stringValue: String) { self.stringValue = stringValue } var intValue: Int? init?(intValue: Int) { return nil } // 固定で返ってくるキー static let total_hit_count = CodingKeys(stringValue: "total_hit_count")! } init(from decoder: Decoder) throws { let values = try decoder.container(keyedBy: CodingKeys.self) total_hit_count = try values.decode(Int.self, forKey: .total_hit_count) // NOTE: 数字部分の仕様がわからないので 0 〜 hit_count までデータを取得する reviews = try (0 ..< total_hit_count) .map { (index) -> Review? in try values.decodeIfPresent(Review.self, forKey: CodingKeys(stringValue: "\(index)")!) } .compactMap { $0 } } } struct Store: Decodable { let shop_id: String let shop_name: String }