Swift JSON解码报错:hourly_rate键不存在问题求助
解码Laravel API返回JSON时的keyNotFound错误
API返回的JSON数据
[ { "id": 1, "name": "instructor", "hourly_rate": 35.01, "bio": "Patient and experienced driving instructor specialising in new drivers", "profile_image": null, "rating": 4.8, "total_reviews": 25, "location": { "latitude": 51.5074, "longitude": -0.1278 }, "qualifications": [ "ADI Certificate", "Grade A Instructor" ], "years_experience": 5, "gender": "female", "transmission": "Manual", "vehicle_type": null, "lesson_types": [ "standard", "intensive", "pass_plus" ], "is_available": true } ]
Swift Instructor Codable模型
struct Instructor: Identifiable, Codable { let id: Int let name: String let rating: Double let hourlyRate: Double let experience: Int let profileImage: String? let description: String let availability: [Availability] let gender: InstructorGender let lessonTypes: [LessonType] let transmission: TransmissionType let vehicleType: String? let qualifications: [String] let isAvailable: Bool struct Availability: Codable { let dayOfWeek: Int let startTime: Date let endTime: Date } enum TransmissionType: String, Codable { case manual = "Manual" case automatic = "Automatic" } enum LessonType: String, Codable { case standard = "standard" case intensive = "intensive" case passPlus = "pass_plus" } enum InstructorGender: String, Codable { case male case female init(from decoder: Decoder) throws { let container = try decoder.singleValueContainer() let rawValue = try container.decode(String.self).lowercased() switch rawValue { case "male": self = .male case "female": self = .female default: throw DecodingError.dataCorruptedError(in: container, debugDescription: "Invalid gender value: \(rawValue)") } } } enum CodingKeys: String, CodingKey { case id case name case rating case hourlyRate = "hourly_rate" case experience = "years_experience" case profileImage = "profile_image" case description = "bio" case availability case gender case lessonTypes = "lesson_types" case transmission case vehicleType = "vehicle_type" case qualifications case isAvailable = "is_available" } }
解码器与请求代码
private let jsonDecoder: JSONDecoder = { let decoder = JSONDecoder() decoder.dateDecodingStrategy = .iso8601 decoder.keyDecodingStrategy = .convertFromSnakeCase return decoder }() func getBookings(token: String) async throws -> [Booking] { guard let url = URL(string: "\(baseURL)/bookings") else { throw NetworkError.invalidURL } let data = try await sendAuthenticatedRequestWithData(to: url, method: .get, token: token) if let jsonString = String(data: data, encoding: .utf8) { print("📱 Raw Bookings JSON:", jsonString) } do { let bookings = try jsonDecoder.decode([Booking].self, from: data) print("📱 Successfully decoded \(bookings.count) bookings") return bookings } catch { print("❌ Decoding error: \(error)") throw error } }
错误信息(中文翻译)
未找到键CodingKeys(stringValue: "hourly_rate", intValue: nil),解码路径为[_JSONKey(stringValue: "Index 0", intValue: 0)],调试描述:没有与键"hourly_rate"关联的值。
问题分析与解决方案
核心问题
- 键解码策略冲突:同时设置了
decoder.keyDecodingStrategy = .convertFromSnakeCase和手动CodingKeys映射,自动转换逻辑与手动映射冲突,导致解码器无法正确匹配字段。 - 模型字段与JSON不匹配:
Instructor模型中的availability字段在API返回的JSON中不存在,会触发解码失败。 - 请求接口与模型不对应:调用的
/bookings接口返回的是教练数据,却用[Booking].self解码,完全不匹配。
解决方案
统一键解码策略:保留
.convertFromSnakeCase,简化CodingKeys,只保留非自动转换的映射(比如bio→description):enum CodingKeys: String, CodingKey { case id, name, rating, hourlyRate, experience, profileImage case description = "bio" case availability, gender, lessonTypes, transmission, vehicleType, qualifications, isAvailable }此时
hourly_rate会自动转为hourlyRate,无需手动映射。处理不存在的字段:将模型中的
availability改为可选类型,或直接删除该字段:let availability: [Availability]? // 改为可选类型匹配接口与模型:修改请求方法,调用教练数据接口并解码
[Instructor].self:func getInstructors(token: String) async throws -> [Instructor] { guard let url = URL(string: "\(baseURL)/instructors") else { throw NetworkError.invalidURL } let data = try await sendAuthenticatedRequestWithData(to: url, method: .get, token: token) if let jsonString = String(data: data, encoding: .utf8) { print("📱 Raw Instructors JSON:", jsonString) } do { let instructors = try jsonDecoder.decode([Instructor].self, from: data) print("📱 Successfully decoded \(instructors.count) instructors") return instructors } catch { print("❌ Decoding error: \(error)") throw error } }
内容的提问来源于stack exchange,提问作者user3524311
相关产品推荐
相关产品推荐

