Swift Codable解析带动态键的JSON响应时第二个对象解析为nil问题
问题原因
- 你在自定义解码逻辑中使用
try?静默吞掉了RelatedProductPrice类型的解码错误,无法定位具体失败点。从现象来看大概率是RelatedProductPrice的字段映射不匹配(例如下划线格式的JSON键和驼峰格式的属性名没有正确映射、字段类型定义错误),刚好第一个对象符合映射规则,第二个对象映射失败。 - 手写的动态键解码逻辑冗余,且
DynamicCodingKeys的整型初始化方法直接返回nil,存在潜在的数字类型键解析异常风险。
解决方案
方案1:简化模型,无需手写自定义解码逻辑
直接用[String: RelatedProductPrice]数组承接related_product_price_info的内容,完全避开自定义解码的出错概率:
首先定义基础价格模型:
struct RelatedProductPrice: Codable { let price: Double let discountPercentage: Double let relatedProductGroupId: Int let discountedPrice: Double enum CodingKeys: String, CodingKey { case price case discountPercentage = "discount_percentage" case relatedProductGroupId = "related_product_group_id" case discountedPrice = "discounted_price" } }
修改根模型定义:
struct ProductSearchResult: Codable { let searchResult: String? let relatedProductPriceInfo: [[String: RelatedProductPrice]]? enum CodingKeys: String, CodingKey { case searchResult = "search_result" case relatedProductPriceInfo = "related_product_price_info" } }
解析代码(显式捕获错误,方便定位问题):
let decoder = JSONDecoder() // 如果所有字段都要自动转换下划线到驼峰,也可以开启这个配置,不需要单独写CodingKeys // decoder.keyDecodingStrategy = .convertFromSnakeCase do { let result = try decoder.decode(ProductSearchResult.self, from: data) // 取值示例 result.relatedProductPriceInfo?.forEach { itemDict in guard let (groupId, priceInfo) = itemDict.first else { return } print("动态键:\(groupId),价格数据:\(priceInfo)") } } catch { // 解码出错会打印具体错误信息,直接定位问题字段 print("解码失败:\(error)") }
方案2:保留原有模型结构,修复解码逻辑
如果需要保留RelatedProductPriceInfo的封装结构,修改其自定义解码实现,不要静默吞错,同时修复DynamicCodingKeys的实现:
struct RelatedProductPriceInfo: Codable { var relatedProductPrice: RelatedProductPrice? private struct DynamicCodingKeys: CodingKey { var stringValue: String init?(stringValue: String) { self.stringValue = stringValue } // 修复整型键的初始化逻辑,避免数字类动态键解析失败 var intValue: Int? init?(intValue: Int) { self.stringValue = "\(intValue)" self.intValue = intValue } } init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: DynamicCodingKeys.self) guard let firstKey = container.allKeys.first else { return } // 不要用try?,出错直接抛出,方便排查 relatedProductPrice = try container.decode(RelatedProductPrice.self, forKey: firstKey) } }
内容的提问来源于stack exchange,提问作者vikash sahu
相关产品推荐
相关产品推荐

