Swift 4中JSONDecoder解析动态键名JSON的实现方法
刚好我之前处理过类似的场景,CoinmarketCap这种根据用户选择动态返回不同price_xxx键的JSON,确实需要灵活点的解码方式,下面给你两种实用的方案,你可以根据需求选:
方案一:提前枚举已知货币键(类型安全)
如果你的应用只支持有限几种货币(比如欧元、英镑、美元),这种方案最稳妥,类型安全而且容易维护:
struct CryptoPrice: Decodable { let price: Double let currency: String // 提前把支持的货币键都列在这里 private enum CodingKeys: String, CodingKey { case priceEur = "price_eur" case priceGbp = "price_gbp" case priceUsd = "price_usd" } init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) // 依次检查哪个键存在,解析对应的值和货币类型 if let eurPriceStr = try container.decodeIfPresent(String.self, forKey: .priceEur), let priceVal = Double(eurPriceStr) { price = priceVal currency = "EUR" } else if let gbpPriceStr = try container.decodeIfPresent(String.self, forKey: .priceGbp), let priceVal = Double(gbpPriceStr) { price = priceVal currency = "GBP" } else if let usdPriceStr = try container.decodeIfPresent(String.self, forKey: .priceUsd), let priceVal = Double(usdPriceStr) { price = priceVal currency = "USD" } else { throw DecodingError.dataCorruptedError( forKey: .priceEur, in: container, debugDescription: "未找到支持的价格字段" ) } } }
优点:编译时就能检查键名是否正确,不容易出错;缺点:如果要新增支持的货币,需要手动添加对应的CodingKey和判断分支。
方案二:动态匹配price_开头的键(自动适配新货币)
如果你的应用需要支持CoinmarketCap所有的货币,或者不想每次加货币都改代码,那可以用动态CodingKey的方式,自动识别所有price_开头的键:
struct CryptoPrice: Decodable { let price: Double let currency: String init(from decoder: Decoder) throws { // 用自定义的动态CodingKey容器 let container = try decoder.container(keyedBy: DynamicCodingKey.self) // 遍历所有键,找到以price_开头的字段 for key in container.allKeys { guard key.stringValue.starts(with: "price_") else { continue } // 解析价格字符串并转成Double let priceStr = try container.decode(String.self, forKey: key) guard let priceVal = Double(priceStr) else { throw DecodingError.dataCorruptedError( forKey: key, in: container, debugDescription: "价格格式无效:\(priceStr)" ) } // 提取货币代码(比如price_eur -> EUR) let currencyCode = key.stringValue.replacingOccurrences(of: "price_", with: "").uppercased() price = priceVal currency = currencyCode return // 找到第一个符合的就返回,因为API每次只会返回一个价格键 } // 如果遍历完没找到任何price_开头的键,抛出错误 throw DecodingError.dataCorrupted(DecodingError.Context( codingPath: [], debugDescription: "响应中未找到价格字段" )) } // 自定义动态CodingKey,支持任意字符串键 private struct DynamicCodingKey: CodingKey { let stringValue: String init?(stringValue: String) { self.stringValue = stringValue } let intValue: Int? init?(intValue: Int) { self.intValue = intValue self.stringValue = String(intValue) } } }
优点:不用提前知道所有货币,自动适配API新增的货币类型;缺点:编译时无法检查键名,需要确保API返回的键格式符合price_xxx的规则。
如何使用
不管用哪种方案,解析数组类型的JSON都很简单:
// 假设你已经拿到了API返回的Data(比如通过URLSession请求) let jsonData: Data = ... do { let priceList = try JSONDecoder().decode([CryptoPrice].self, from: jsonData) if let firstPrice = priceList.first { print("当前价格:\(firstPrice.price) \(firstPrice.currency)") } } catch { print("解析失败:\(error.localizedDescription)") }
注意:CoinmarketCap返回的价格是字符串类型,所以我们需要先转成String再转成Double,直接解码成Double可能会失败哦。
内容的提问来源于stack exchange,提问作者Tarvo Mäesepp
相关产品推荐
相关产品推荐

