如何创建模型解码含字符串格式字典的API响应?JsonDecoder映射失败
问题排查与解决方案
核心错误原因
你遇到的解码失败问题,首要原因是缺少根模型:API响应的最外层是一个包含additional_data键的对象,但你直接尝试解码AdditionalData类型,JSONDecoder无法匹配根层级的结构,导致解码失败。
其次,虽然checklist_done和work_with_price是字符串化的JSON,理论上用String?类型可以接收,但如果没有正确处理外层结构,同样会失败。
正确的模型实现方案
1. 新增根模型
首先创建对应API最外层结构的根模型:
struct APIResponse: Codable { let additionalData: AdditionalData enum CodingKeys: String, CodingKey { case additionalData = "additional_data" } }
2. 优化AdditionalData模型
将字符串化的JSON字段直接转为字典类型([String: String]?),通过自定义解码逻辑自动解析,比直接存字符串更实用:
struct AdditionalData: Codable { var id, bookingID: Int? var checklistDone: [String: String]? var workWithPrice: [String: String]? var workPrice: String? var estimatedPrice: Int? var discountCode, mechanicProfit: String? var discount, ccavenueCharge, ccavenueGst: Int? var totalPrice: String? var paymentMethod, gateway, paymentResponse: String? var status: String? var paymentmode, bankname: String? var txnid, banktxnid: String? var txndate: String? var createdAt, updatedAt: String? var adminDiscount: Int? enum CodingKeys: String, CodingKey { case id case bookingID = "booking_id" case checklistDone = "checklist_done" case workWithPrice = "work_with_price" case workPrice = "work_price" case estimatedPrice = "estimated_price" case discountCode = "discount_code" case mechanicProfit = "mechanic_profit" case discount case ccavenueCharge = "ccavenue_charge" case ccavenueGst = "ccavenue_gst" case totalPrice = "total_price" case paymentMethod = "payment_method" case gateway case paymentResponse = "payment_response" case status case paymentmode, bankname, txnid, banktxnid, txndate case createdAt = "created_at" case updatedAt = "updated_at" case adminDiscount = "admin_discount" } init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) // 解码常规字段 id = try container.decodeIfPresent(Int.self, forKey: .id) bookingID = try container.decodeIfPresent(Int.self, forKey: .bookingID) workPrice = try container.decodeIfPresent(String.self, forKey: .workPrice) estimatedPrice = try container.decodeIfPresent(Int.self, forKey: .estimatedPrice) discountCode = try container.decodeIfPresent(String.self, forKey: .discountCode) mechanicProfit = try container.decodeIfPresent(String.self, forKey: .mechanicProfit) discount = try container.decodeIfPresent(Int.self, forKey: .discount) ccavenueCharge = try container.decodeIfPresent(Int.self, forKey: .ccavenueCharge) ccavenueGst = try container.decodeIfPresent(Int.self, forKey: .ccavenueGst) totalPrice = try container.decodeIfPresent(String.self, forKey: .totalPrice) paymentMethod = try container.decodeIfPresent(String.self, forKey: .paymentMethod) gateway = try container.decodeIfPresent(String.self, forKey: .gateway) paymentResponse = try container.decodeIfPresent(String.self, forKey: .paymentResponse) status = try container.decodeIfPresent(String.self, forKey: .status) paymentmode = try container.decodeIfPresent(String.self, forKey: .paymentmode) bankname = try container.decodeIfPresent(String.self, forKey: .bankname) txnid = try container.decodeIfPresent(String.self, forKey: .txnid) banktxnid = try container.decodeIfPresent(String.self, forKey: .banktxnid) txndate = try container.decodeIfPresent(String.self, forKey: .txndate) createdAt = try container.decodeIfPresent(String.self, forKey: .createdAt) updatedAt = try container.decodeIfPresent(String.self, forKey: .updatedAt) adminDiscount = try container.decodeIfPresent(Int.self, forKey: .adminDiscount) // 解码字符串化的JSON字段,添加错误处理避免整体解码失败 if let checklistStr = try container.decodeIfPresent(String.self, forKey: .checklistDone) { do { checklistDone = try JSONDecoder().decode([String: String].self, from: checklistStr.data(using: .utf8)!) } catch { print("checklist_done 解析失败: \(error)") checklistDone = nil } } if let workPriceStr = try container.decodeIfPresent(String.self, forKey: .workWithPrice) { do { workWithPrice = try JSONDecoder().decode([String: String].self, from: workPriceStr.data(using: .utf8)!) } catch { print("work_with_price 解析失败: \(error)") workWithPrice = nil } } } }
3. 解码示例
使用根模型进行解码:
do { let apiResponse = try JSONDecoder().decode(APIResponse.self, from: yourJsonData) // 访问解析后的数据 print("完成的检查项: \(apiResponse.additionalData.checklistDone ?? [:])") print("带价格的工单: \(apiResponse.additionalData.workWithPrice ?? [:])") } catch { print("整体解码失败: \(error)") }
备选方案:坚持用String接收字符串化JSON
如果必须保留checklistDone和workWithPrice为String?类型,只需新增根模型,无需修改AdditionalData的解码逻辑,解码时同样使用根模型即可:
// 修改后的AdditionalData(保留String类型) struct AdditionalData: Codable { var id, bookingID: Int? var checklistDone: String? var workWithPrice: String? // ... 其他字段保持不变,CodingKeys也不变 } // 解码方式 do { let apiResponse = try JSONDecoder().decode(APIResponse.self, from: yourJsonData) // 手动解析字符串化JSON if let checklistStr = apiResponse.additionalData.checklistDone { let checklistDict = try JSONDecoder().decode([String: String].self, from: checklistStr.data(using: .utf8)!) print(checklistDict) } } catch { print(error) }
内容的提问来源于stack exchange,提问作者Arish
相关产品推荐
相关产品推荐

